diff options
author | Denis Anisimov <denis@vaadin.com> | 2014-09-21 21:04:31 +0300 |
---|---|---|
committer | Leif Åstrand <leif@vaadin.com> | 2014-12-16 21:45:22 +0000 |
commit | a9f24b00e9ddcd5ca19ac2907e0bf2413f036af4 (patch) | |
tree | 8b0cc88751226ea65672afa625169c56b00cf5fe | |
parent | ac31951998701ad20f8f2c80510ade754b167359 (diff) | |
download | vaadin-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.java | 51 | ||||
-rw-r--r-- | server/src/com/vaadin/data/util/BeanItem.java | 107 | ||||
-rw-r--r-- | server/src/com/vaadin/data/util/BeanUtil.java | 177 |
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; + } + } +} |