You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ReflectTools.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.util;
  17. import java.beans.IntrospectionException;
  18. import java.beans.PropertyDescriptor;
  19. import java.io.Serializable;
  20. import java.lang.reflect.Field;
  21. import java.lang.reflect.InvocationTargetException;
  22. import java.lang.reflect.Method;
  23. import java.lang.reflect.Modifier;
  24. /**
  25. * An util class with helpers for reflection operations. Used internally by
  26. * Vaadin and should not be used by application developers. Subject to change at
  27. * any time.
  28. *
  29. * @since 6.2
  30. */
  31. public class ReflectTools implements Serializable {
  32. static final String CREATE_INSTANCE_FAILED = "Unable to create an instance of '%s'. Make sure it has a no-arg constructor";
  33. static final String CREATE_INSTANCE_FAILED_FOR_NON_STATIC_MEMBER_CLASS = "Unable to create an instance of '%s'. Make sure the class is static if it is a nested class.";
  34. static final String CREATE_INSTANCE_FAILED_ACCESS_EXCEPTION = "Unable to create an instance of '%s'. Make sure the class is public and that is has a public no-arg constructor.";
  35. static final String CREATE_INSTANCE_FAILED_NO_PUBLIC_NOARG_CONSTRUCTOR = "Unable to create an instance of '%s'. Make sure the class has a public no-arg constructor.";
  36. static final String CREATE_INSTANCE_FAILED_LOCAL_CLASS = "Cannot instantiate local class '%s'. Move class declaration outside the method.";
  37. static final String CREATE_INSTANCE_FAILED_CONSTRUCTOR_THREW_EXCEPTION = "Unable to create an instance of '%s'. The constructor threw an exception.";
  38. /**
  39. * Locates the method in the given class. Returns null if the method is not
  40. * found. Throws an ExceptionInInitializerError if there is a problem
  41. * locating the method as this is mainly called from static blocks.
  42. *
  43. * @param cls
  44. * Class that contains the method
  45. * @param methodName
  46. * The name of the method
  47. * @param parameterTypes
  48. * The parameter types for the method.
  49. * @return A reference to the method
  50. * @throws ExceptionInInitializerError
  51. * Wraps any exception in an {@link ExceptionInInitializerError}
  52. * so this method can be called from a static initializer.
  53. */
  54. public static Method findMethod(Class<?> cls, String methodName,
  55. Class<?>... parameterTypes) throws ExceptionInInitializerError {
  56. try {
  57. return cls.getDeclaredMethod(methodName, parameterTypes);
  58. } catch (Exception e) {
  59. throw new ExceptionInInitializerError(e);
  60. }
  61. }
  62. /**
  63. * Returns the value of the java field.
  64. * <p>
  65. * Uses getter if present, otherwise tries to access even private fields
  66. * directly.
  67. *
  68. * @param object
  69. * The object containing the field
  70. * @param field
  71. * The field we want to get the value for
  72. * @return The value of the field in the object
  73. * @throws InvocationTargetException
  74. * If the value could not be retrieved
  75. * @throws IllegalAccessException
  76. * If the value could not be retrieved
  77. * @throws IllegalArgumentException
  78. * If the value could not be retrieved
  79. */
  80. public static Object getJavaFieldValue(Object object, Field field)
  81. throws IllegalArgumentException, IllegalAccessException,
  82. InvocationTargetException {
  83. PropertyDescriptor pd;
  84. try {
  85. pd = new PropertyDescriptor(field.getName(), object.getClass());
  86. Method getter = pd.getReadMethod();
  87. if (getter != null) {
  88. return getter.invoke(object, (Object[]) null);
  89. }
  90. } catch (IntrospectionException e1) {
  91. // Ignore this and try to get directly using the field
  92. }
  93. // Try to get the value or throw an exception
  94. if (!field.isAccessible()) {
  95. // Try to gain access even if field is private
  96. field.setAccessible(true);
  97. }
  98. return field.get(object);
  99. }
  100. /**
  101. * Returns the value of the java field that is assignable to the property
  102. * type.
  103. * <p>
  104. * Uses getter if a getter for the correct return type is present, otherwise
  105. * tries to access even private fields directly. If the java field is not
  106. * assignable to the property type throws an IllegalArgumentException.
  107. *
  108. * @param object
  109. * The object containing the field
  110. * @param field
  111. * The field we want to get the value for
  112. * @param propertyType
  113. * The type the field must be assignable to
  114. * @return The value of the field in the object
  115. * @throws InvocationTargetException
  116. * If the value could not be retrieved
  117. * @throws IllegalAccessException
  118. * If the value could not be retrieved
  119. * @throws IllegalArgumentException
  120. * If the value could not be retrieved
  121. */
  122. public static Object getJavaFieldValue(Object object, Field field,
  123. Class<?> propertyType) throws IllegalArgumentException,
  124. IllegalAccessException, InvocationTargetException {
  125. PropertyDescriptor pd;
  126. try {
  127. pd = new PropertyDescriptor(field.getName(), object.getClass());
  128. if (propertyType.isAssignableFrom(pd.getPropertyType())) {
  129. Method getter = pd.getReadMethod();
  130. if (getter != null) {
  131. return getter.invoke(object, (Object[]) null);
  132. }
  133. }
  134. } catch (IntrospectionException e1) {
  135. // Ignore this and try to get directly using the field
  136. }
  137. // If the field's type cannot be casted in to the requested type
  138. if (!propertyType.isAssignableFrom(field.getType())) {
  139. throw new IllegalArgumentException();
  140. }
  141. // Try to get the value or throw an exception
  142. if (!field.isAccessible()) {
  143. // Try to gain access even if field is private
  144. field.setAccessible(true);
  145. }
  146. return field.get(object);
  147. }
  148. /**
  149. * Sets the value of a java field.
  150. * <p>
  151. * Uses setter if present, otherwise tries to access even private fields
  152. * directly.
  153. *
  154. * @param object
  155. * The object containing the field
  156. * @param field
  157. * The field we want to set the value for
  158. * @param value
  159. * The value to set
  160. * @throws IllegalAccessException
  161. * If the value could not be assigned to the field
  162. * @throws IllegalArgumentException
  163. * If the value could not be assigned to the field
  164. * @throws InvocationTargetException
  165. * If the value could not be assigned to the field
  166. */
  167. public static void setJavaFieldValue(Object object, Field field,
  168. Object value) throws IllegalAccessException,
  169. IllegalArgumentException, InvocationTargetException {
  170. PropertyDescriptor pd;
  171. try {
  172. pd = new PropertyDescriptor(field.getName(), object.getClass());
  173. Method setter = pd.getWriteMethod();
  174. if (setter != null) {
  175. // Exceptions are thrown forward if this fails
  176. setter.invoke(object, value);
  177. }
  178. } catch (IntrospectionException e1) {
  179. // Ignore this and try to set directly using the field
  180. }
  181. // Try to set the value directly to the field or throw an exception
  182. if (!field.isAccessible()) {
  183. // Try to gain access even if field is private
  184. field.setAccessible(true);
  185. }
  186. field.set(object, value);
  187. }
  188. /**
  189. * @since 7.4
  190. */
  191. public static Class<?> convertPrimitiveType(Class<?> type) {
  192. // Gets the return type from get method
  193. if (type.isPrimitive()) {
  194. if (type.equals(Boolean.TYPE)) {
  195. type = Boolean.class;
  196. } else if (type.equals(Integer.TYPE)) {
  197. type = Integer.class;
  198. } else if (type.equals(Float.TYPE)) {
  199. type = Float.class;
  200. } else if (type.equals(Double.TYPE)) {
  201. type = Double.class;
  202. } else if (type.equals(Byte.TYPE)) {
  203. type = Byte.class;
  204. } else if (type.equals(Character.TYPE)) {
  205. type = Character.class;
  206. } else if (type.equals(Short.TYPE)) {
  207. type = Short.class;
  208. } else if (type.equals(Long.TYPE)) {
  209. type = Long.class;
  210. }
  211. }
  212. return type;
  213. }
  214. private ReflectTools() {
  215. }
  216. /**
  217. * Finds the most specific class that both provided classes extend from.
  218. *
  219. * @param a
  220. * one class to get the base type for, not <code>null</code>
  221. * @param b
  222. * another class to get the base type for, not <code>null</code>
  223. * @return the most specific base class, not <code>null</code>
  224. *
  225. * @since 8.0
  226. */
  227. public static Class<?> findCommonBaseType(Class<?> a, Class<?> b) {
  228. if (a.isInterface()) {
  229. throw new IllegalArgumentException("a cannot be an interface");
  230. }
  231. if (b.isInterface()) {
  232. throw new IllegalArgumentException("b cannot be an interface");
  233. }
  234. if (a.isAssignableFrom(b)) {
  235. return a;
  236. } else if (b.isAssignableFrom(a)) {
  237. return b;
  238. }
  239. Class<?> currentClass = a;
  240. while (!currentClass.isAssignableFrom(b)) {
  241. currentClass = currentClass.getSuperclass();
  242. }
  243. return currentClass;
  244. }
  245. /**
  246. * Creates a instance of the given class with a no-arg constructor.
  247. * <p>
  248. * Catches all exceptions which might occur and wraps them in a
  249. * {@link IllegalArgumentException} with a descriptive error message hinting
  250. * of what might be wrong with the class that could not be instantiated.
  251. *
  252. * @param cls
  253. * the class to instantiate
  254. * @return an instance of the class
  255. * @since 8.1.1
  256. */
  257. public static <T> T createInstance(Class<T> cls) {
  258. checkClassAccessibility(cls);
  259. try {
  260. return cls.getConstructor().newInstance();
  261. } catch (NoSuchMethodException e) {
  262. throw new IllegalArgumentException(String.format(
  263. CREATE_INSTANCE_FAILED_NO_PUBLIC_NOARG_CONSTRUCTOR,
  264. cls.getName()), e);
  265. } catch (InstantiationException e) {
  266. if (cls.isMemberClass() && !Modifier.isStatic(cls.getModifiers())) {
  267. throw new IllegalArgumentException(String.format(
  268. CREATE_INSTANCE_FAILED_FOR_NON_STATIC_MEMBER_CLASS,
  269. cls.getName()), e);
  270. } else {
  271. throw new IllegalArgumentException(
  272. String.format(CREATE_INSTANCE_FAILED, cls.getName()),
  273. e);
  274. }
  275. } catch (IllegalAccessException e) {
  276. throw new IllegalArgumentException(String.format(
  277. CREATE_INSTANCE_FAILED_ACCESS_EXCEPTION, cls.getName()), e);
  278. } catch (IllegalArgumentException e) {
  279. throw new IllegalArgumentException(
  280. String.format(CREATE_INSTANCE_FAILED, cls.getName()), e);
  281. } catch (InvocationTargetException e) {
  282. throw new IllegalArgumentException(String.format(
  283. CREATE_INSTANCE_FAILED_CONSTRUCTOR_THREW_EXCEPTION,
  284. cls.getName()), e);
  285. }
  286. }
  287. /**
  288. * Makes a check whether the provided class is externally accessible for
  289. * instantiation (e.g. it's not inner class (nested and not static) and is
  290. * not a local class).
  291. *
  292. * @param cls
  293. * type to check
  294. */
  295. private static void checkClassAccessibility(Class<?> cls) {
  296. if (cls.isMemberClass() && !Modifier.isStatic(cls.getModifiers())) {
  297. throw new IllegalArgumentException(String.format(
  298. CREATE_INSTANCE_FAILED_FOR_NON_STATIC_MEMBER_CLASS,
  299. cls.getName()));
  300. } else if (cls.isLocalClass()) {
  301. throw new IllegalArgumentException(String
  302. .format(CREATE_INSTANCE_FAILED_LOCAL_CLASS, cls.getName()));
  303. }
  304. }
  305. /**
  306. * Returns the first non-synthetic method of the specified
  307. * {@code listenerClass}, which must have single method in the source-code.
  308. *
  309. * This is needed, to remove the synthetic methods added if the class is
  310. * instrumented.
  311. *
  312. * @param listenerClass
  313. * The {@link Class} of the listener, which has a single method
  314. * in the source code
  315. * @return the first non-synthetic method
  316. * @throws IllegalStateException
  317. * if the specified class does not have found method
  318. * @since 8.2
  319. */
  320. public static Method getMethod(Class<?> listenerClass) {
  321. for (Method m : listenerClass.getDeclaredMethods()) {
  322. if (!m.isSynthetic()) {
  323. return m;
  324. }
  325. }
  326. throw new IllegalStateException("Class " + listenerClass.getName()
  327. + " does not have a method.");
  328. }
  329. }