Advertisement
Guest User

Untitled

a guest
May 21st, 2016
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.15 KB | None | 0 0
  1. package de.klein5.TowerDefense.datawatcher;
  2.  
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.Field;
  5. import java.lang.reflect.Method;
  6. import java.util.Arrays;
  7. import java.util.regex.Matcher;
  8. import java.util.regex.Pattern;
  9.  
  10. import org.bukkit.Bukkit;
  11.  
  12. /**
  13. * An utility class that simplifies reflection in Bukkit plugins.
  14. *
  15. * @author Kristian
  16. */
  17. public final class Reflection {
  18. /**
  19. * An interface for invoking a specific constructor.
  20. */
  21. public interface ConstructorInvoker {
  22. /**
  23. * Invoke a constructor for a specific class.
  24. *
  25. * @param arguments - the arguments to pass to the constructor.
  26. * @return The constructed object.
  27. */
  28. public Object invoke(Object... arguments);
  29. }
  30.  
  31. /**
  32. * An interface for invoking a specific method.
  33. */
  34. public interface MethodInvoker {
  35. /**
  36. * Invoke a method on a specific target object.
  37. *
  38. * @param target - the target object, or NULL for a static method.
  39. * @param arguments - the arguments to pass to the method.
  40. * @return The return value, or NULL if is void.
  41. */
  42. public Object invoke(Object target, Object... arguments);
  43. }
  44.  
  45. /**
  46. * An interface for retrieving the field content.
  47. *
  48. * @param <T> - field type.
  49. */
  50. public interface FieldAccessor<T> {
  51. /**
  52. * Retrieve the content of a field.
  53. *
  54. * @param target - the target object, or NULL for a static field.
  55. * @return The value of the field.
  56. */
  57. public T get(Object target);
  58.  
  59. /**
  60. * Set the content of a field.
  61. *
  62. * @param target - the target object, or NULL for a static field.
  63. * @param value - the new value of the field.
  64. */
  65. public void set(Object target, Object value);
  66.  
  67. /**
  68. * Determine if the given object has this field.
  69. *
  70. * @param target - the object to test.
  71. * @return TRUE if it does, FALSE otherwise.
  72. */
  73. public boolean hasField(Object target);
  74. }
  75.  
  76. // Deduce the net.minecraft.server.v* package
  77. private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
  78. private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
  79. private static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "");
  80.  
  81. // Variable replacement
  82. private static Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}");
  83.  
  84. private Reflection() {
  85. // Seal class
  86. }
  87.  
  88. /**
  89. * Retrieve a field accessor for a specific field type and name.
  90. *
  91. * @param target - the target type.
  92. * @param name - the name of the field, or NULL to ignore.
  93. * @param fieldType - a compatible field type.
  94. * @return The field accessor.
  95. */
  96. public static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType) {
  97. return getField(target, name, fieldType, 0);
  98. }
  99.  
  100. /**
  101. * Retrieve a field accessor for a specific field type and name.
  102. *
  103. * @param className - lookup name of the class, see {@link #getClass(String)}.
  104. * @param name - the name of the field, or NULL to ignore.
  105. * @param fieldType - a compatible field type.
  106. * @return The field accessor.
  107. */
  108. public static <T> FieldAccessor<T> getField(String className, String name, Class<T> fieldType) {
  109. return getField(getClass(className), name, fieldType, 0);
  110. }
  111.  
  112. /**
  113. * Retrieve a field accessor for a specific field type and name.
  114. *
  115. * @param target - the target type.
  116. * @param fieldType - a compatible field type.
  117. * @param index - the number of compatible fields to skip.
  118. * @return The field accessor.
  119. */
  120. public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index) {
  121. return getField(target, null, fieldType, index);
  122. }
  123.  
  124. /**
  125. * Retrieve a field accessor for a specific field type and name.
  126. *
  127. * @param className - lookup name of the class, see {@link #getClass(String)}.
  128. * @param fieldType - a compatible field type.
  129. * @param index - the number of compatible fields to skip.
  130. * @return The field accessor.
  131. */
  132. public static <T> FieldAccessor<T> getField(String className, Class<T> fieldType, int index) {
  133. return getField(getClass(className), fieldType, index);
  134. }
  135.  
  136. // Common method
  137. private static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType, int index) {
  138. for (final Field field : target.getDeclaredFields()) {
  139. if ((name == null || field.getName().equals(name)) && fieldType.isAssignableFrom(field.getType()) && index-- <= 0) {
  140. field.setAccessible(true);
  141.  
  142. // A function for retrieving a specific field value
  143. return new FieldAccessor<T>() {
  144.  
  145. @Override
  146. @SuppressWarnings("unchecked")
  147. public T get(Object target) {
  148. try {
  149. return (T) field.get(target);
  150. } catch (IllegalAccessException e) {
  151. throw new RuntimeException("Cannot access reflection.", e);
  152. }
  153. }
  154.  
  155. @Override
  156. public void set(Object target, Object value) {
  157. try {
  158. field.set(target, value);
  159. } catch (IllegalAccessException e) {
  160. throw new RuntimeException("Cannot access reflection.", e);
  161. }
  162. }
  163.  
  164. @Override
  165. public boolean hasField(Object target) {
  166. // target instanceof DeclaringClass
  167. return field.getDeclaringClass().isAssignableFrom(target.getClass());
  168. }
  169. };
  170. }
  171. }
  172.  
  173. // Search in parent classes
  174. if (target.getSuperclass() != null)
  175. return getField(target.getSuperclass(), name, fieldType, index);
  176.  
  177. throw new IllegalArgumentException("Cannot find field with type " + fieldType);
  178. }
  179.  
  180. /**
  181. * Search for the first publically and privately defined method of the given name and parameter count.
  182. *
  183. * @param className - lookup name of the class, see {@link #getClass(String)}.
  184. * @param methodName - the method name, or NULL to skip.
  185. * @param params - the expected parameters.
  186. * @return An object that invokes this specific method.
  187. * @throws IllegalStateException If we cannot find this method.
  188. */
  189. public static MethodInvoker getMethod(String className, String methodName, Class<?>... params) {
  190. return getTypedMethod(getClass(className), methodName, null, params);
  191. }
  192.  
  193. /**
  194. * Search for the first publically and privately defined method of the given name and parameter count.
  195. *
  196. * @param clazz - a class to start with.
  197. * @param methodName - the method name, or NULL to skip.
  198. * @param params - the expected parameters.
  199. * @return An object that invokes this specific method.
  200. * @throws IllegalStateException If we cannot find this method.
  201. */
  202. public static MethodInvoker getMethod(Class<?> clazz, String methodName, Class<?>... params) {
  203. return getTypedMethod(clazz, methodName, null, params);
  204. }
  205.  
  206. /**
  207. * Search for the first publically and privately defined method of the given name and parameter count.
  208. *
  209. * @param clazz - a class to start with.
  210. * @param methodName - the method name, or NULL to skip.
  211. * @param returnType - the expected return type, or NULL to ignore.
  212. * @param params - the expected parameters.
  213. * @return An object that invokes this specific method.
  214. * @throws IllegalStateException If we cannot find this method.
  215. */
  216. public static MethodInvoker getTypedMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... params) {
  217. for (final Method method : clazz.getDeclaredMethods()) {
  218. if ((methodName == null || method.getName().equals(methodName))
  219. && (returnType == null || method.getReturnType().equals(returnType))
  220. && Arrays.equals(method.getParameterTypes(), params)) {
  221. method.setAccessible(true);
  222.  
  223. return new MethodInvoker() {
  224.  
  225. @Override
  226. public Object invoke(Object target, Object... arguments) {
  227. try {
  228. return method.invoke(target, arguments);
  229. } catch (Exception e) {
  230. throw new RuntimeException("Cannot invoke method " + method, e);
  231. }
  232. }
  233.  
  234. };
  235. }
  236. }
  237.  
  238. // Search in every superclass
  239. if (clazz.getSuperclass() != null)
  240. return getMethod(clazz.getSuperclass(), methodName, params);
  241.  
  242. throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params)));
  243. }
  244.  
  245. /**
  246. * Search for the first publically and privately defined constructor of the given name and parameter count.
  247. *
  248. * @param className - lookup name of the class, see {@link #getClass(String)}.
  249. * @param params - the expected parameters.
  250. * @return An object that invokes this constructor.
  251. * @throws IllegalStateException If we cannot find this method.
  252. */
  253. public static ConstructorInvoker getConstructor(String className, Class<?>... params) {
  254. return getConstructor(getClass(className), params);
  255. }
  256.  
  257. /**
  258. * Search for the first publically and privately defined constructor of the given name and parameter count.
  259. *
  260. * @param clazz - a class to start with.
  261. * @param params - the expected parameters.
  262. * @return An object that invokes this constructor.
  263. * @throws IllegalStateException If we cannot find this method.
  264. */
  265. public static ConstructorInvoker getConstructor(Class<?> clazz, Class<?>... params) {
  266. for (final Constructor<?> constructor : clazz.getDeclaredConstructors()) {
  267. if (Arrays.equals(constructor.getParameterTypes(), params)) {
  268. constructor.setAccessible(true);
  269.  
  270. return new ConstructorInvoker() {
  271.  
  272. @Override
  273. public Object invoke(Object... arguments) {
  274. try {
  275. return constructor.newInstance(arguments);
  276. } catch (Exception e) {
  277. throw new RuntimeException("Cannot invoke constructor " + constructor, e);
  278. }
  279. }
  280.  
  281. };
  282. }
  283. }
  284.  
  285. throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
  286. }
  287.  
  288. /**
  289. * Retrieve a class from its full name, without knowing its type on compile time.
  290. * <p>
  291. * This is useful when looking up fields by a NMS or OBC type.
  292. * <p>
  293. *
  294. * @see {@link #getClass()} for more information.
  295. * @param lookupName - the class name with variables.
  296. * @return The class.
  297. */
  298. public static Class<Object> getUntypedClass(String lookupName) {
  299. @SuppressWarnings({ "rawtypes", "unchecked" })
  300. Class<Object> clazz = (Class) getClass(lookupName);
  301. return clazz;
  302. }
  303.  
  304. /**
  305. * Retrieve a class from its full name.
  306. * <p>
  307. * Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
  308. * <p>
  309. * <table border="1">
  310. * <tr>
  311. * <th>Variable</th>
  312. * <th>Content</th>
  313. * </tr>
  314. * <tr>
  315. * <td>{nms}</td>
  316. * <td>Actual package name of net.minecraft.server.VERSION</td>
  317. * </tr>
  318. * <tr>
  319. * <td>{obc}</td>
  320. * <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
  321. * </tr>
  322. * <tr>
  323. * <td>{version}</td>
  324. * <td>The current Minecraft package VERSION, if any.</td>
  325. * </tr>
  326. * </table>
  327. *
  328. * @param lookupName - the class name with variables.
  329. * @return The looked up class.
  330. * @throws IllegalArgumentException If a variable or class could not be found.
  331. */
  332. public static Class<?> getClass(String lookupName) {
  333. return getCanonicalClass(expandVariables(lookupName));
  334. }
  335.  
  336. /**
  337. * Retrieve a class in the net.minecraft.server.VERSION.* package.
  338. *
  339. * @param name - the name of the class, excluding the package.
  340. * @throws IllegalArgumentException If the class doesn't exist.
  341. */
  342. public static Class<?> getMinecraftClass(String name) {
  343. return getCanonicalClass(NMS_PREFIX + "." + name);
  344. }
  345.  
  346. /**
  347. * Retrieve a class in the org.bukkit.craftbukkit.VERSION.* package.
  348. *
  349. * @param name - the name of the class, excluding the package.
  350. * @throws IllegalArgumentException If the class doesn't exist.
  351. */
  352. public static Class<?> getCraftBukkitClass(String name) {
  353. return getCanonicalClass(OBC_PREFIX + "." + name);
  354. }
  355.  
  356. /**
  357. * Retrieve a class by its canonical name.
  358. *
  359. * @param canonicalName - the canonical name.
  360. * @return The class.
  361. */
  362. private static Class<?> getCanonicalClass(String canonicalName) {
  363. try {
  364. return Class.forName(canonicalName);
  365. } catch (ClassNotFoundException e) {
  366. throw new IllegalArgumentException("Cannot find " + canonicalName, e);
  367. }
  368. }
  369.  
  370. /**
  371. * Expand variables such as "{nms}" and "{obc}" to their corresponding packages.
  372. *
  373. * @param name - the full name of the class.
  374. * @return The expanded string.
  375. */
  376. private static String expandVariables(String name) {
  377. StringBuffer output = new StringBuffer();
  378. Matcher matcher = MATCH_VARIABLE.matcher(name);
  379.  
  380. while (matcher.find()) {
  381. String variable = matcher.group(1);
  382. String replacement = "";
  383.  
  384. // Expand all detected variables
  385. if ("nms".equalsIgnoreCase(variable))
  386. replacement = NMS_PREFIX;
  387. else if ("obc".equalsIgnoreCase(variable))
  388. replacement = OBC_PREFIX;
  389. else if ("version".equalsIgnoreCase(variable))
  390. replacement = VERSION;
  391. else
  392. throw new IllegalArgumentException("Unknown variable: " + variable);
  393.  
  394. // Assume the expanded variables are all packages, and append a dot
  395. if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.')
  396. replacement += ".";
  397. matcher.appendReplacement(output, Matcher.quoteReplacement(replacement));
  398. }
  399.  
  400. matcher.appendTail(output);
  401. return output.toString();
  402. }
  403. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement