aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis Anisimov <denis@vaadin.com>2014-09-21 21:04:31 +0300
committerLeif Åstrand <leif@vaadin.com>2014-12-16 21:45:22 +0000
commita9f24b00e9ddcd5ca19ac2907e0bf2413f036af4 (patch)
tree8b0cc88751226ea65672afa625169c56b00cf5fe
parentac31951998701ad20f8f2c80510ade754b167359 (diff)
downloadvaadin-framework-a9f24b00e9ddcd5ca19ac2907e0bf2413f036af4.tar.gz
vaadin-framework-a9f24b00e9ddcd5ca19ac2907e0bf2413f036af4.zip
Use introspection instead of reflection to get property type (#10672).
Change-Id: I4eebad640c3c091bfff335bae8ef455e662ad4cb
-rw-r--r--server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java51
-rw-r--r--server/src/com/vaadin/data/util/BeanItem.java107
-rw-r--r--server/src/com/vaadin/data/util/BeanUtil.java177
3 files changed, 194 insertions, 141 deletions
diff --git a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java
index 257a958f3a..23a72ee1e5 100644
--- a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java
+++ b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java
@@ -15,18 +15,20 @@
*/
package com.vaadin.data.fieldgroup;
+import java.beans.IntrospectionException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import com.vaadin.data.Item;
import com.vaadin.data.util.BeanItem;
+import com.vaadin.data.util.BeanUtil;
import com.vaadin.data.validator.BeanValidator;
import com.vaadin.ui.Field;
public class BeanFieldGroup<T> extends FieldGroup {
- private Class<T> beanType;
+ private final Class<T> beanType;
private static Boolean beanValidationImplementationAvailable = null;
private final Map<Field<?>, BeanValidator> defaultValidators;
@@ -47,17 +49,20 @@ public class BeanFieldGroup<T> extends FieldGroup {
* form "fieldName" or "fieldName.subField[.subField2]" but the
* method declaration comes from parent.
*/
- java.lang.reflect.Field f;
try {
- f = getField(beanType, propertyId.toString());
- return f.getType();
- } catch (SecurityException e) {
- throw new BindException("Cannot determine type of propertyId '"
- + propertyId + "'.", e);
- } catch (NoSuchFieldException e) {
+ Class<?> type = BeanUtil.getPropertyType(beanType,
+ propertyId.toString());
+ if (type == null) {
+ throw new BindException(
+ "Cannot determine type of propertyId '"
+ + propertyId
+ + "'. The propertyId was not found in "
+ + beanType.getName());
+ }
+ return type;
+ } catch (IntrospectionException e) {
throw new BindException("Cannot determine type of propertyId '"
- + propertyId + "'. The propertyId was not found in "
- + beanType.getName(), e);
+ + propertyId + "'. Unable to introspect " + beanType, e);
}
}
}
@@ -79,32 +84,6 @@ public class BeanFieldGroup<T> extends FieldGroup {
return null;
}
- private static java.lang.reflect.Field getField(Class<?> cls,
- String propertyId) throws SecurityException, NoSuchFieldException {
- if (propertyId.contains(".")) {
- String[] parts = propertyId.split("\\.", 2);
- // Get the type of the field in the "cls" class
- java.lang.reflect.Field field1 = getField(cls, parts[0]);
- // Find the rest from the sub type
- return getField(field1.getType(), parts[1]);
- } else {
- try {
- // Try to find the field directly in the given class
- java.lang.reflect.Field field1 = cls
- .getDeclaredField(propertyId);
- return field1;
- } catch (NoSuchFieldException e) {
- // Try super classes until we reach Object
- Class<?> superClass = cls.getSuperclass();
- if (superClass != null && superClass != Object.class) {
- return getField(superClass, propertyId);
- } else {
- throw e;
- }
- }
- }
- }
-
private static String getFieldName(Class<?> cls, String propertyId)
throws SecurityException, NoSuchFieldException {
for (java.lang.reflect.Field field1 : cls.getDeclaredFields()) {
diff --git a/server/src/com/vaadin/data/util/BeanItem.java b/server/src/com/vaadin/data/util/BeanItem.java
index 64f30261c2..1be8b70f47 100644
--- a/server/src/com/vaadin/data/util/BeanItem.java
+++ b/server/src/com/vaadin/data/util/BeanItem.java
@@ -16,12 +16,8 @@
package com.vaadin.data.util;
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -189,7 +185,8 @@ public class BeanItem<BT> extends PropertysetItem {
// Try to introspect, if it fails, we just have an empty Item
try {
- List<PropertyDescriptor> propertyDescriptors = getBeanPropertyDescriptor(beanClass);
+ List<PropertyDescriptor> propertyDescriptors = BeanUtil
+ .getBeanPropertyDescriptor(beanClass);
// Add all the bean properties as MethodProperties to this Item
// later entries on the list overwrite earlier ones
@@ -210,106 +207,6 @@ public class BeanItem<BT> extends PropertysetItem {
}
/**
- * Returns the property descriptors of a class or an interface.
- *
- * For an interface, superinterfaces are also iterated as Introspector does
- * not take them into account (Oracle Java bug 4275879), but in that case,
- * both the setter and the getter for a property must be in the same
- * interface and should not be overridden in subinterfaces for the discovery
- * to work correctly.
- *
- * For interfaces, the iteration is depth first and the properties of
- * superinterfaces are returned before those of their subinterfaces.
- *
- * @param beanClass
- * @return
- * @throws IntrospectionException
- */
- private static List<PropertyDescriptor> getBeanPropertyDescriptor(
- final Class<?> beanClass) throws IntrospectionException {
- // Oracle bug 4275879: Introspector does not consider superinterfaces of
- // an interface
- if (beanClass.isInterface()) {
- List<PropertyDescriptor> propertyDescriptors = new ArrayList<PropertyDescriptor>();
-
- for (Class<?> cls : beanClass.getInterfaces()) {
- propertyDescriptors.addAll(getBeanPropertyDescriptor(cls));
- }
-
- BeanInfo info = Introspector.getBeanInfo(beanClass);
- propertyDescriptors.addAll(getPropertyDescriptors(info));
-
- return propertyDescriptors;
- } else {
- BeanInfo info = Introspector.getBeanInfo(beanClass);
- return getPropertyDescriptors(info);
- }
- }
-
- // Workaround for Java6 bug JDK-6788525. Do nothing for JDK7+.
- private static List<PropertyDescriptor> getPropertyDescriptors(
- BeanInfo beanInfo) {
- PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
- List<PropertyDescriptor> result = new ArrayList<PropertyDescriptor>(
- descriptors.length);
- for (PropertyDescriptor descriptor : descriptors) {
- try {
- Method readMethod = getMethodFromBridge(descriptor
- .getReadMethod());
- if (readMethod != null) {
- Method writeMethod = getMethodFromBridge(
- descriptor.getWriteMethod(),
- readMethod.getReturnType());
- if (writeMethod == null) {
- writeMethod = descriptor.getWriteMethod();
- }
- PropertyDescriptor descr = new PropertyDescriptor(
- descriptor.getName(), readMethod, writeMethod);
- result.add(descr);
- } else {
- result.add(descriptor);
- }
- } catch (SecurityException ignore) {
- // handle next descriptor
- } catch (IntrospectionException e) {
- result.add(descriptor);
- }
- }
- return result;
- }
-
- /**
- * Return not bridged method for bridge {@code bridgeMethod} method. If
- * method {@code bridgeMethod} is not bridge method then return null.
- */
- private static Method getMethodFromBridge(Method bridgeMethod)
- throws SecurityException {
- if (bridgeMethod == null) {
- return null;
- }
- return getMethodFromBridge(bridgeMethod,
- bridgeMethod.getParameterTypes());
- }
-
- /**
- * Return not bridged method for bridge {@code bridgeMethod} method and
- * declared {@code paramTypes}. If method {@code bridgeMethod} is not bridge
- * method then return null.
- */
- private static Method getMethodFromBridge(Method bridgeMethod,
- Class<?>... paramTypes) throws SecurityException {
- if (bridgeMethod == null || !bridgeMethod.isBridge()) {
- return null;
- }
- try {
- return bridgeMethod.getDeclaringClass().getMethod(
- bridgeMethod.getName(), paramTypes);
- } catch (NoSuchMethodException e) {
- return null;
- }
- }
-
- /**
* Expands nested bean properties by replacing a top-level property with
* some or all of its sub-properties. The expansion is not recursive.
*
diff --git a/server/src/com/vaadin/data/util/BeanUtil.java b/server/src/com/vaadin/data/util/BeanUtil.java
new file mode 100644
index 0000000000..e2f85a765c
--- /dev/null
+++ b/server/src/com/vaadin/data/util/BeanUtil.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.data.util;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for Java Beans information access.
+ *
+ * @author Vaadin Ltd
+ */
+public final class BeanUtil implements Serializable {
+ // Prevent instantiation of util class
+ private BeanUtil() {
+ }
+
+ /**
+ * Returns the property descriptors of a class or an interface.
+ *
+ * For an interface, superinterfaces are also iterated as Introspector does
+ * not take them into account (Oracle Java bug 4275879), but in that case,
+ * both the setter and the getter for a property must be in the same
+ * interface and should not be overridden in subinterfaces for the discovery
+ * to work correctly.
+ *
+ * NOTE : This utility method relies on introspection (and returns
+ * PropertyDescriptor) which is a part of java.beans package. The latter
+ * package could require bigger JDK in the future (with Java 9+). So it may
+ * be changed in the future.
+ *
+ * For interfaces, the iteration is depth first and the properties of
+ * superinterfaces are returned before those of their subinterfaces.
+ *
+ * @param beanClass
+ * @return
+ * @throws IntrospectionException
+ */
+ public static List<PropertyDescriptor> getBeanPropertyDescriptor(
+ final Class<?> beanClass) throws IntrospectionException {
+ // Oracle bug 4275879: Introspector does not consider superinterfaces of
+ // an interface
+ if (beanClass.isInterface()) {
+ List<PropertyDescriptor> propertyDescriptors = new ArrayList<PropertyDescriptor>();
+
+ for (Class<?> cls : beanClass.getInterfaces()) {
+ propertyDescriptors.addAll(getBeanPropertyDescriptor(cls));
+ }
+
+ BeanInfo info = Introspector.getBeanInfo(beanClass);
+ propertyDescriptors.addAll(getPropertyDescriptors(info));
+
+ return propertyDescriptors;
+ } else {
+ BeanInfo info = Introspector.getBeanInfo(beanClass);
+ return getPropertyDescriptors(info);
+ }
+ }
+
+ /**
+ * Returns {@code propertyId} class for property declared in {@code clazz}.
+ * Property could be of form "property.subProperty[.subProperty2]" i.e.
+ * refer to some nested property.
+ *
+ * @param clazz
+ * class where property is declared
+ * @param propertyId
+ * property of form "property" or
+ * "property.subProperty[.subProperty2]"
+ * @return class of the property
+ * @throws IntrospectionException
+ */
+ public static Class<?> getPropertyType(Class<?> clazz, String propertyId)
+ throws IntrospectionException {
+ if (propertyId.contains(".")) {
+ String[] parts = propertyId.split("\\.", 2);
+ // Get the type of the field in the "cls" class
+ Class<?> propertyBean = getPropertyType(clazz, parts[0]);
+ // Find the rest from the sub type
+ return getPropertyType(propertyBean, parts[1]);
+ } else {
+ List<PropertyDescriptor> descriptors = getBeanPropertyDescriptor(clazz);
+
+ for (PropertyDescriptor descriptor : descriptors) {
+ final Method getMethod = descriptor.getReadMethod();
+ if (descriptor.getName().equals(propertyId)
+ && getMethod != null
+ && getMethod.getDeclaringClass() != Object.class) {
+ return descriptor.getPropertyType();
+ }
+ }
+ return null;
+ }
+ }
+
+ // Workaround for Java6 bug JDK-6788525. Do nothing for JDK7+.
+ private static List<PropertyDescriptor> getPropertyDescriptors(
+ BeanInfo beanInfo) {
+ PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
+ List<PropertyDescriptor> result = new ArrayList<PropertyDescriptor>(
+ descriptors.length);
+ for (PropertyDescriptor descriptor : descriptors) {
+ try {
+ Method readMethod = getMethodFromBridge(descriptor
+ .getReadMethod());
+ if (readMethod != null) {
+ Method writeMethod = getMethodFromBridge(
+ descriptor.getWriteMethod(),
+ readMethod.getReturnType());
+ if (writeMethod == null) {
+ writeMethod = descriptor.getWriteMethod();
+ }
+ PropertyDescriptor descr = new PropertyDescriptor(
+ descriptor.getName(), readMethod, writeMethod);
+ result.add(descr);
+ } else {
+ result.add(descriptor);
+ }
+ } catch (SecurityException ignore) {
+ // handle next descriptor
+ } catch (IntrospectionException e) {
+ result.add(descriptor);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Return declared method for which {@code bridgeMethod} is generated. If
+ * {@code bridgeMethod} is not a bridge method then return null.
+ */
+ private static Method getMethodFromBridge(Method bridgeMethod)
+ throws SecurityException {
+ if (bridgeMethod == null) {
+ return null;
+ }
+ return getMethodFromBridge(bridgeMethod,
+ bridgeMethod.getParameterTypes());
+ }
+
+ /**
+ * Return declared method for which {@code bridgeMethod} is generated using
+ * its {@code paramTypes}. If {@code bridgeMethod} is not a bridge method
+ * then return null.
+ */
+ private static Method getMethodFromBridge(Method bridgeMethod,
+ Class<?>... paramTypes) throws SecurityException {
+ if (bridgeMethod == null || !bridgeMethod.isBridge()) {
+ return null;
+ }
+ try {
+ return bridgeMethod.getDeclaringClass().getMethod(
+ bridgeMethod.getName(), paramTypes);
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+}