/** * There are some nasty bugs for introspection with generics. This method addresses those nasty * bugs and tries to find proper methods if available * http://bugs.sun.com/view_bug.do?bug_id=6788525 http://bugs.sun.com/view_bug.do?bug_id=6528714 * * @param descriptor * @return */ private static PropertyDescriptor fixGenericDescriptor( Class<?> clazz, PropertyDescriptor descriptor) { Method readMethod = descriptor.getReadMethod(); Method writeMethod = descriptor.getWriteMethod(); if (readMethod != null && (readMethod.isBridge() || readMethod.isSynthetic())) { String propertyName = descriptor.getName(); // capitalize the first letter of the string; String baseName = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1); String setMethodName = "set" + baseName; String getMethodName = "get" + baseName; Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.getName().equals(getMethodName) && !method.isBridge() && !method.isSynthetic()) { try { descriptor.setReadMethod(method); } catch (IntrospectionException e) { // move on } } if (method.getName().equals(setMethodName) && !method.isBridge() && !method.isSynthetic()) { try { descriptor.setWriteMethod(method); } catch (IntrospectionException e) { // move on } } } } return descriptor; }
/** * Gets the method that should be used to read the property value. * * @return The method that should be used to read the property value. May return null if the * property can't be read. */ public synchronized Method getReadMethod() { Method readMethod = getReadMethod0(); if (readMethod == null) { Class cls = getClass0(); if (cls == null || (readMethodName == null && readMethodRef == null)) { // The read method was explicitly set to null. return null; } if (readMethodName == null) { Class type = getPropertyType0(); if (type == boolean.class || type == null) { readMethodName = "is" + getBaseName(); } else { readMethodName = "get" + getBaseName(); } } // Since there can be multiple write methods but only one getter // method, find the getter method first so that you know what the // property type is. For booleans, there can be "is" and "get" // methods. If an "is" method exists, this is the official // reader method so look for this one first. readMethod = Introspector.findMethod(cls, readMethodName, 0); if (readMethod == null) { readMethodName = "get" + getBaseName(); readMethod = Introspector.findMethod(cls, readMethodName, 0); } try { setReadMethod(readMethod); } catch (IntrospectionException ex) { // fall } } return readMethod; }
public static Map<String, PropertyDescriptor> propertyDescriptorsByName( final Class<?> beanClass) { final Map<String, PropertyDescriptor> map = new TreeMap<String, PropertyDescriptor>(); for (Method method : beanClass.getMethods()) { final boolean readMethod = isReadMethod(method); final boolean writeMethod = !readMethod && isWriteMethod(method); // LOGGER.debug("isReadMethod({}) => {}", method, readMethod); // LOGGER.debug("isWriteMethod({}) => {}", method, writeMethod); if (!(readMethod || writeMethod)) { continue; } final String name = propertyName(method); LOGGER.debug("{} => {}", method, name); try { PropertyDescriptor descriptor = map.get(name); if (descriptor == null) { map.put(name, descriptor = new PropertyDescriptor(name, beanClass, null, null)); } if (readMethod) { descriptor.setReadMethod(method); } else { descriptor.setWriteMethod(method); } } catch (IntrospectionException x) { throw new RuntimeException(x); } } return Collections.unmodifiableMap(map); }
/** * This constructor takes the name of a simple property, and Method objects for reading and * writing the property. * * @param propertyName The programmatic name of the property. * @param readMethod The method used for reading the property value. May be null if the property * is write-only. * @param writeMethod The method used for writing the property value. May be null if the property * is read-only. * @exception IntrospectionException if an exception occurs during introspection. */ public PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException { if (propertyName == null || propertyName.length() == 0) { throw new IntrospectionException("bad property name"); } setName(propertyName); setReadMethod(readMethod); setWriteMethod(writeMethod); }
/** * Creates <code>PropertyDescriptor</code> for the specified bean with the specified name and * methods to read/write the property value. * * @param bean the type of the target bean * @param base the base name of the property (the rest of the method name) * @param read the method used for reading the property value * @param write the method used for writing the property value * @exception IntrospectionException if an exception occurs during introspection * @since 1.7 */ PropertyDescriptor(Class<?> bean, String base, @Nullable Method read, @Nullable Method write) throws IntrospectionException { if (bean == null) { throw new IntrospectionException("Target Bean class is null"); } setClass0(bean); setName(Introspector.decapitalize(base)); setReadMethod(read); setWriteMethod(write); this.baseName = base; }
static PropertyDescriptor[] getInterfacePropertyDescriptors(Class<?> interfaceClass) { List<PropertyDescriptor> propDescriptors = new ArrayList<PropertyDescriptor>(); // Add prop descriptors for interface passed in propDescriptors.addAll(Arrays.asList(BeanUtils.getPropertyDescriptors(interfaceClass))); // Look for interface inheritance. If super interfaces are found, recurse up the hierarchy tree // and add prop // descriptors for each interface found. // PropertyUtils.getPropertyDescriptors() does not correctly walk the inheritance hierarchy for // interfaces. Class<?>[] interfaces = interfaceClass.getInterfaces(); if (interfaces != null) { for (Class<?> superInterfaceClass : interfaces) { List<PropertyDescriptor> superInterfacePropertyDescriptors = Arrays.asList(getInterfacePropertyDescriptors(superInterfaceClass)); /* * #1814758 * Check for existing descriptor with the same name to prevent 2 property descriptors with the same name being added * to the result list. This caused issues when getter and setter of an attribute on different interfaces in * an inheritance hierarchy */ for (PropertyDescriptor superPropDescriptor : superInterfacePropertyDescriptors) { PropertyDescriptor existingPropDescriptor = findPropDescriptorByName(propDescriptors, superPropDescriptor.getName()); if (existingPropDescriptor == null) { propDescriptors.add(superPropDescriptor); } else { try { if (existingPropDescriptor.getReadMethod() == null) { existingPropDescriptor.setReadMethod(superPropDescriptor.getReadMethod()); } if (existingPropDescriptor.getWriteMethod() == null) { existingPropDescriptor.setWriteMethod(superPropDescriptor.getWriteMethod()); } } catch (IntrospectionException e) { throw new MappingException(e); } } } } } return propDescriptors.toArray(new PropertyDescriptor[propDescriptors.size()]); }
/** * Gets the method that should be used to read the property value. * * @return The method that should be used to read the property value. May return null if the * property can't be read. */ @Pure public synchronized @Nullable Method getReadMethod() { Method readMethod = this.readMethodRef.get(); if (readMethod == null) { Class<?> cls = getClass0(); if (cls == null || (readMethodName == null && !this.readMethodRef.isSet())) { // The read method was explicitly set to null. return null; } String nextMethodName = Introspector.GET_PREFIX + getBaseName(); if (readMethodName == null) { Class<?> type = getPropertyType0(); if (type == boolean.class || type == null) { readMethodName = Introspector.IS_PREFIX + getBaseName(); } else { readMethodName = nextMethodName; } } // Since there can be multiple write methods but only one getter // method, find the getter method first so that you know what the // property type is. For booleans, there can be "is" and "get" // methods. If an "is" method exists, this is the official // reader method so look for this one first. readMethod = Introspector.findMethod(cls, readMethodName, 0); if ((readMethod == null) && !readMethodName.equals(nextMethodName)) { readMethodName = nextMethodName; readMethod = Introspector.findMethod(cls, readMethodName, 0); } try { setReadMethod(readMethod); } catch (IntrospectionException ex) { // fall } } return readMethod; }
/** * Package-private constructor. Merge two property descriptors. Where they conflict, give the * second argument (y) priority over the first argument (x). * * @param x The first (lower priority) PropertyDescriptor * @param y The second (higher priority) PropertyDescriptor */ PropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) { super(x, y); if (y.baseName != null) { baseName = y.baseName; } else { baseName = x.baseName; } if (y.readMethodName != null) { readMethodName = y.readMethodName; } else { readMethodName = x.readMethodName; } if (y.writeMethodName != null) { writeMethodName = y.writeMethodName; } else { writeMethodName = x.writeMethodName; } if (y.propertyTypeRef != null) { propertyTypeRef = y.propertyTypeRef; } else { propertyTypeRef = x.propertyTypeRef; } // Figure out the merged read method. Method xr = x.getReadMethod(); Method yr = y.getReadMethod(); // Normally give priority to y's readMethod. try { if (yr != null && yr.getDeclaringClass() == getClass0()) { setReadMethod(yr); } else { setReadMethod(xr); } } catch (IntrospectionException ex) { // fall through } // However, if both x and y reference read methods in the same class, // give priority to a boolean "is" method over a boolean "get" method. if (xr != null && yr != null && xr.getDeclaringClass() == yr.getDeclaringClass() && xr.getReturnType() == boolean.class && yr.getReturnType() == boolean.class && xr.getName().indexOf("is") == 0 && yr.getName().indexOf("get") == 0) { try { setReadMethod(xr); } catch (IntrospectionException ex) { // fall through } } Method xw = x.getWriteMethod(); Method yw = y.getWriteMethod(); try { if (yw != null && yw.getDeclaringClass() == getClass0()) { setWriteMethod(yw); } else { setWriteMethod(xw); } } catch (IntrospectionException ex) { // Fall through } if (y.getPropertyEditorClass() != null) { setPropertyEditorClass(y.getPropertyEditorClass()); } else { setPropertyEditorClass(x.getPropertyEditorClass()); } bound = x.bound | y.bound; constrained = x.constrained | y.constrained; }
private void addOrUpdatePropertyDescriptor( String propertyName, Method readMethod, Method writeMethod, Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException { for (PropertyDescriptor existingPD : this.propertyDescriptors) { if (existingPD.getName().equals(propertyName)) { // is there already a descriptor that captures this read method or its corresponding write // method? if (existingPD.getReadMethod() != null) { if (readMethod != null && existingPD.getReadMethod().getReturnType() != readMethod.getReturnType() || writeMethod != null && existingPD.getReadMethod().getReturnType() != writeMethod.getParameterTypes()[0]) { // no -> add a new descriptor for it below break; } } // update the existing descriptor's read method if (readMethod != null) { try { existingPD.setReadMethod(readMethod); } catch (IntrospectionException ex) { // there is a conflicting setter method present -> null it out and try again existingPD.setWriteMethod(null); existingPD.setReadMethod(readMethod); } } // is there already a descriptor that captures this write method or its corresponding read // method? if (existingPD.getWriteMethod() != null) { if (readMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != readMethod.getReturnType() || writeMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != writeMethod.getParameterTypes()[0]) { // no -> add a new descriptor for it below break; } } // update the existing descriptor's write method if (writeMethod != null) { existingPD.setWriteMethod(writeMethod); } // is this descriptor indexed? if (existingPD instanceof IndexedPropertyDescriptor) { IndexedPropertyDescriptor existingIPD = (IndexedPropertyDescriptor) existingPD; // is there already a descriptor that captures this indexed read method or its // corresponding indexed write method? if (existingIPD.getIndexedReadMethod() != null) { if (indexedReadMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedReadMethod.getReturnType() || indexedWriteMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedWriteMethod.getParameterTypes()[1]) { // no -> add a new descriptor for it below break; } } // update the existing descriptor's indexed read method try { if (indexedReadMethod != null) { existingIPD.setIndexedReadMethod(indexedReadMethod); } } catch (IntrospectionException ex) { // there is a conflicting indexed setter method present -> null it out and try again existingIPD.setIndexedWriteMethod(null); existingIPD.setIndexedReadMethod(indexedReadMethod); } // is there already a descriptor that captures this indexed write method or its // corresponding indexed read method? if (existingIPD.getIndexedWriteMethod() != null) { if (indexedReadMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedReadMethod.getReturnType() || indexedWriteMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedWriteMethod.getParameterTypes()[1]) { // no -> add a new descriptor for it below break; } } // update the existing descriptor's indexed write method if (indexedWriteMethod != null) { existingIPD.setIndexedWriteMethod(indexedWriteMethod); } } // the descriptor has been updated -> return immediately return; } } // we haven't yet seen read or write methods for this property -> add a new descriptor if (indexedReadMethod == null && indexedWriteMethod == null) { this.propertyDescriptors.add(new PropertyDescriptor(propertyName, readMethod, writeMethod)); } else { this.propertyDescriptors.add( new IndexedPropertyDescriptor( propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod)); } }