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.

BeanUtil.java 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. * Copyright 2000-2016 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.data.util;
  17. import java.beans.BeanInfo;
  18. import java.beans.IntrospectionException;
  19. import java.beans.Introspector;
  20. import java.beans.PropertyDescriptor;
  21. import java.io.Serializable;
  22. import java.lang.reflect.Method;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. /**
  26. * Utility class for Java Beans information access.
  27. *
  28. * @since 7.4
  29. *
  30. * @author Vaadin Ltd
  31. */
  32. public final class BeanUtil implements Serializable {
  33. // Prevent instantiation of util class
  34. private BeanUtil() {
  35. }
  36. /**
  37. * Returns the property descriptors of a class or an interface.
  38. *
  39. * For an interface, superinterfaces are also iterated as Introspector does
  40. * not take them into account (Oracle Java bug 4275879), but in that case,
  41. * both the setter and the getter for a property must be in the same
  42. * interface and should not be overridden in subinterfaces for the discovery
  43. * to work correctly.
  44. * <p>
  45. * NOTE : This utility method relies on introspection (and returns
  46. * PropertyDescriptor) which is a part of java.beans package. The latter
  47. * package could require bigger JDK in the future (with Java 9+). So it may
  48. * be changed in the future.
  49. * <p>
  50. * For interfaces, the iteration is depth first and the properties of
  51. * superinterfaces are returned before those of their subinterfaces.
  52. *
  53. * @param beanType
  54. * the type whose properties to query
  55. * @return a list of property descriptors of the given type
  56. * @throws IntrospectionException
  57. * if the introspection fails
  58. */
  59. public static List<PropertyDescriptor> getBeanPropertyDescriptors(
  60. final Class<?> beanType) throws IntrospectionException {
  61. // Oracle bug 4275879: Introspector does not consider superinterfaces of
  62. // an interface
  63. if (beanType.isInterface()) {
  64. List<PropertyDescriptor> propertyDescriptors = new ArrayList<>();
  65. for (Class<?> cls : beanType.getInterfaces()) {
  66. propertyDescriptors.addAll(getBeanPropertyDescriptors(cls));
  67. }
  68. BeanInfo info = Introspector.getBeanInfo(beanType);
  69. propertyDescriptors.addAll(getPropertyDescriptors(info));
  70. return propertyDescriptors;
  71. } else {
  72. BeanInfo info = Introspector.getBeanInfo(beanType);
  73. return getPropertyDescriptors(info);
  74. }
  75. }
  76. /**
  77. * Returns the type of the property with the given name and declaring class.
  78. * The property name may refer to a nested property, eg.
  79. * "property.subProperty" or "property.subProperty1.subProperty2". The
  80. * property must have a public read method (or a chain of read methods in
  81. * case of a nested property).
  82. *
  83. * @param beanType
  84. * the type declaring the property
  85. * @param propertyName
  86. * the name of the property
  87. * @return the property type
  88. * @throws IntrospectionException
  89. * if the introspection fails
  90. */
  91. public static Class<?> getPropertyType(Class<?> beanType,
  92. String propertyName) throws IntrospectionException {
  93. PropertyDescriptor descriptor = getPropertyDescriptor(beanType,
  94. propertyName);
  95. if (descriptor != null) {
  96. return descriptor.getPropertyType();
  97. } else {
  98. return null;
  99. }
  100. }
  101. /**
  102. * Returns the property descriptor for the property of the given name and
  103. * declaring class. The property name may refer to a nested property, eg.
  104. * "property.subProperty" or "property.subProperty1.subProperty2". The
  105. * property must have a public read method (or a chain of read methods in
  106. * case of a nested property).
  107. *
  108. * @param beanType
  109. * the type declaring the property
  110. * @param propertyName
  111. * the name of the property
  112. * @return the corresponding descriptor
  113. * @throws IntrospectionException
  114. * if the introspection fails
  115. */
  116. public static PropertyDescriptor getPropertyDescriptor(Class<?> beanType,
  117. String propertyName) throws IntrospectionException {
  118. if (propertyName.contains(".")) {
  119. String[] parts = propertyName.split("\\.", 2);
  120. // Get the type of the field in the bean class
  121. Class<?> propertyBean = getPropertyType(beanType, parts[0]);
  122. // Find the rest from the sub type
  123. return getPropertyDescriptor(propertyBean, parts[1]);
  124. } else {
  125. List<PropertyDescriptor> descriptors = getBeanPropertyDescriptors(
  126. beanType);
  127. for (PropertyDescriptor descriptor : descriptors) {
  128. final Method getMethod = descriptor.getReadMethod();
  129. if (descriptor.getName().equals(propertyName)
  130. && getMethod != null
  131. && getMethod.getDeclaringClass() != Object.class) {
  132. return descriptor;
  133. }
  134. }
  135. return null;
  136. }
  137. }
  138. // Workaround for Java6 bug JDK-6788525. Do nothing for JDK7+.
  139. private static List<PropertyDescriptor> getPropertyDescriptors(
  140. BeanInfo beanInfo) {
  141. PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
  142. List<PropertyDescriptor> result = new ArrayList<>(descriptors.length);
  143. for (PropertyDescriptor descriptor : descriptors) {
  144. try {
  145. Method readMethod = getMethodFromBridge(
  146. descriptor.getReadMethod());
  147. if (readMethod != null) {
  148. Method writeMethod = getMethodFromBridge(
  149. descriptor.getWriteMethod(),
  150. readMethod.getReturnType());
  151. if (writeMethod == null) {
  152. writeMethod = descriptor.getWriteMethod();
  153. }
  154. PropertyDescriptor descr = new PropertyDescriptor(
  155. descriptor.getName(), readMethod, writeMethod);
  156. result.add(descr);
  157. } else {
  158. result.add(descriptor);
  159. }
  160. } catch (SecurityException ignore) {
  161. // handle next descriptor
  162. } catch (IntrospectionException e) {
  163. result.add(descriptor);
  164. }
  165. }
  166. return result;
  167. }
  168. /**
  169. * Return declared method for which {@code bridgeMethod} is generated. If
  170. * {@code bridgeMethod} is not a bridge method then return null.
  171. */
  172. private static Method getMethodFromBridge(Method bridgeMethod)
  173. throws SecurityException {
  174. if (bridgeMethod == null) {
  175. return null;
  176. }
  177. return getMethodFromBridge(bridgeMethod,
  178. bridgeMethod.getParameterTypes());
  179. }
  180. /**
  181. * Return declared method for which {@code bridgeMethod} is generated using
  182. * its {@code paramTypes}. If {@code bridgeMethod} is not a bridge method
  183. * then return null.
  184. */
  185. private static Method getMethodFromBridge(Method bridgeMethod,
  186. Class<?>... paramTypes) throws SecurityException {
  187. if (bridgeMethod == null || !bridgeMethod.isBridge()) {
  188. return null;
  189. }
  190. try {
  191. return bridgeMethod.getDeclaringClass()
  192. .getMethod(bridgeMethod.getName(), paramTypes);
  193. } catch (NoSuchMethodException e) {
  194. return null;
  195. }
  196. }
  197. }