001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.util; 020 021import java.lang.reflect.Constructor; 022import java.lang.reflect.Field; 023import java.lang.reflect.Method; 024 025/** 026 * @author DPOH-VAR 027 * @version 1.0 028 */ 029public class ReflectionUtils { 030 031 /** 032 * prefix of bukkit classes 033 */ 034 private static String preClassB = "org.bukkit.craftbukkit"; 035 /** 036 * prefix of minecraft classes 037 */ 038 private static String preClassM = "net.minecraft.server"; 039 040 public ReflectionUtils(String version) { 041 if (version != null) { 042 preClassB += '.' + version; 043 preClassM += '.' + version; 044 } 045 } 046 047 public static Class<?> getClass(String name) { 048 try { 049 return Class.forName(name); 050 } catch (ClassNotFoundException ignored) { 051 return null; 052 } 053 } 054 055 public static <T> Class<? extends T> getClass(String name, Class<T> superClass) { 056 try { 057 return Class.forName(name).asSubclass(superClass); 058 } catch (ClassCastException | ClassNotFoundException ignored) { 059 return null; 060 } 061 } 062 063 /** 064 * Get class for name. Replace {nms} to net.minecraft.server.V*. Replace {cb} to org.bukkit.craftbukkit.V*. Replace 065 * {nm} to net.minecraft 066 * 067 * @param className possible class paths 068 * @return RefClass object 069 * @throws ClassNotFoundException if no class found 070 */ 071 public static RefClass getRefClass(String className) throws ClassNotFoundException { 072 className = className.replace("{cb}", preClassB).replace("{nms}", preClassM) 073 .replace("{nm}", "net.minecraft"); 074 return getRefClass(Class.forName(className)); 075 } 076 077 /** 078 * get RefClass object by real class 079 * 080 * @param clazz class 081 * @return RefClass based on passed class 082 */ 083 public static RefClass getRefClass(Class<?> clazz) { 084 return new RefClass(clazz); 085 } 086 087 /** 088 * RefClass - utility to simplify work with reflections. 089 */ 090 public static class RefClass { 091 092 private final Class<?> clazz; 093 094 private RefClass(Class<?> clazz) { 095 this.clazz = clazz; 096 } 097 098 /** 099 * get passed class 100 * 101 * @return class 102 */ 103 public Class<?> getRealClass() { 104 return this.clazz; 105 } 106 107 /** 108 * get existing method by name and types 109 * 110 * @param name name 111 * @param types method parameters. can be Class or RefClass 112 * @return RefMethod object 113 * @throws NoSuchMethodException if method not found 114 */ 115 public RefMethod getMethod(String name, Object... types) throws NoSuchMethodException { 116 Class<?>[] classes = new Class[types.length]; 117 int i = 0; 118 for (Object e : types) { 119 if (e instanceof Class) { 120 classes[i++] = (Class<?>) e; 121 } else if (e instanceof RefClass) { 122 classes[i++] = ((RefClass) e).getRealClass(); 123 } else { 124 classes[i++] = e.getClass(); 125 } 126 } 127 try { 128 return new RefMethod(this.clazz.getMethod(name, classes)); 129 } catch (NoSuchMethodException ignored) { 130 return new RefMethod(this.clazz.getDeclaredMethod(name, classes)); 131 } 132 } 133 134 /** 135 * get field by name 136 * 137 * @param name field name 138 * @return RefField 139 * @throws NoSuchFieldException if field not found 140 */ 141 public RefField getField(String name) throws NoSuchFieldException { 142 try { 143 return new RefField(this.clazz.getField(name)); 144 } catch (NoSuchFieldException ignored) { 145 return new RefField(this.clazz.getDeclaredField(name)); 146 } 147 } 148 149 } 150 151 152 /** 153 * Method wrapper 154 */ 155 public static class RefMethod { 156 157 private final Method method; 158 159 private RefMethod(Method method) { 160 this.method = method; 161 method.setAccessible(true); 162 } 163 164 /** 165 * @return passed method 166 */ 167 public Method getRealMethod() { 168 return this.method; 169 } 170 171 /** 172 * apply method to object 173 * 174 * @param e object to which the method is applied 175 * @return RefExecutor with method call(...) 176 */ 177 public RefExecutor of(Object e) { 178 return new RefExecutor(e); 179 } 180 181 182 public class RefExecutor { 183 184 final Object e; 185 186 public RefExecutor(Object e) { 187 this.e = e; 188 } 189 190 /** 191 * apply method for selected object 192 * 193 * @param params sent parameters 194 * @return return value 195 * @throws RuntimeException if something went wrong 196 */ 197 public Object call(Object... params) { 198 try { 199 return RefMethod.this.method.invoke(this.e, params); 200 } catch (Exception e) { 201 throw new RuntimeException(e); 202 } 203 } 204 205 } 206 207 } 208 209 210 /** 211 * Constructor wrapper 212 */ 213 public static class RefConstructor { 214 215 private final Constructor<?> constructor; 216 217 private RefConstructor(Constructor<?> constructor) { 218 this.constructor = constructor; 219 constructor.setAccessible(true); 220 } 221 222 /** 223 * create new instance with constructor 224 * 225 * @param params parameters for constructor 226 * @return new object 227 * @throws ReflectiveOperationException reflective operation exception 228 * @throws IllegalArgumentException illegal argument exception 229 */ 230 public Object create(Object... params) 231 throws ReflectiveOperationException, IllegalArgumentException { 232 return this.constructor.newInstance(params); 233 } 234 235 } 236 237 238 public static class RefField { 239 240 private final Field field; 241 242 private RefField(Field field) { 243 this.field = field; 244 field.setAccessible(true); 245 } 246 247 /** 248 * @return passed field 249 */ 250 public Field getRealField() { 251 return this.field; 252 } 253 254 255 /** 256 * apply fiend for object 257 * 258 * @param e applied object 259 * @return RefExecutor with getter and setter 260 */ 261 public RefExecutor of(Object e) { 262 return new RefExecutor(e); 263 } 264 265 public class RefExecutor { 266 267 final Object e; 268 269 public RefExecutor(Object e) { 270 this.e = e; 271 } 272 273 /** 274 * set field value for applied object 275 * 276 * @param param value 277 */ 278 public void set(Object param) { 279 try { 280 RefField.this.field.set(this.e, param); 281 } catch (Exception e) { 282 throw new RuntimeException(e); 283 } 284 } 285 286 /** 287 * get field value for applied object 288 * 289 * @return value of field 290 */ 291 public Object get() { 292 try { 293 return RefField.this.field.get(this.e); 294 } catch (Exception e) { 295 throw new RuntimeException(e); 296 } 297 } 298 299 } 300 301 } 302 303}