Advertisement
Guest User

Untitled

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