/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.commons.beanutils;

import java.beans.BeanInfo;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

The default BeanIntrospector implementation.

This class implements a default bean introspection algorithm based on the JDK classes in the java.beans package. It discovers properties conforming to the Java Beans specification.

This class is a singleton. The single instance can be obtained using the INSTANCE field. It does not define any state and thus can be shared by arbitrary clients. PropertyUtils per default uses this instance as its only BeanIntrospector object.

Version:$Id$
Since:1.9
/** * <p> * The default {@link BeanIntrospector} implementation. * </p> * <p> * This class implements a default bean introspection algorithm based on the JDK * classes in the <code>java.beans</code> package. It discovers properties * conforming to the Java Beans specification. * </p> * <p> * This class is a singleton. The single instance can be obtained using the * {@code INSTANCE} field. It does not define any state and thus can be * shared by arbitrary clients. {@link PropertyUtils} per default uses this * instance as its only {@code BeanIntrospector} object. * </p> * * @version $Id$ * @since 1.9 */
public class DefaultBeanIntrospector implements BeanIntrospector {
The singleton instance of this class.
/** The singleton instance of this class. */
public static final BeanIntrospector INSTANCE = new DefaultBeanIntrospector();
Constant for argument types of a method that expects no arguments.
/** Constant for argument types of a method that expects no arguments. */
private static final Class<?>[] EMPTY_CLASS_PARAMETERS = new Class[0];
Constant for arguments types of a method that expects a list argument.
/** Constant for arguments types of a method that expects a list argument. */
private static final Class<?>[] LIST_CLASS_PARAMETER = new Class[] { java.util.List.class };
Log instance
/** Log instance */
private final Log log = LogFactory.getLog(getClass());
Private constructor so that no instances can be created.
/** * Private constructor so that no instances can be created. */
private DefaultBeanIntrospector() { }
Performs introspection of a specific Java class. This implementation uses the java.beans.Introspector.getBeanInfo() method to obtain all property descriptors for the current class and adds them to the passed in introspection context.
Params:
  • icontext – the introspection context
/** * Performs introspection of a specific Java class. This implementation uses * the {@code java.beans.Introspector.getBeanInfo()} method to obtain * all property descriptors for the current class and adds them to the * passed in introspection context. * * @param icontext the introspection context */
public void introspect(final IntrospectionContext icontext) { BeanInfo beanInfo = null; try { beanInfo = Introspector.getBeanInfo(icontext.getTargetClass()); } catch (final IntrospectionException e) { // no descriptors are added to the context log.error( "Error when inspecting class " + icontext.getTargetClass(), e); return; } PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); if (descriptors == null) { descriptors = new PropertyDescriptor[0]; } handleIndexedPropertyDescriptors(icontext.getTargetClass(), descriptors); icontext.addPropertyDescriptors(descriptors); }
This method fixes an issue where IndexedPropertyDescriptor behaves differently in different versions of the JDK for 'indexed' properties which use java.util.List (rather than an array). It implements a workaround for Bug 28358. If you have a Bean with the following getters/setters for an indexed property:
public List getFoo()
public Object getFoo(int index)
public void setFoo(List foo)
public void setFoo(int index, Object foo)
then the IndexedPropertyDescriptor's getReadMethod() and getWriteMethod() behave as follows:
  • JDK 1.3.1_04: returns valid Method objects from these methods.
  • JDK 1.4.2_05: returns null from these methods.
Params:
  • beanClass – the current class to be inspected
  • descriptors – the array with property descriptors
/** * This method fixes an issue where IndexedPropertyDescriptor behaves * differently in different versions of the JDK for 'indexed' properties * which use java.util.List (rather than an array). It implements a * workaround for Bug 28358. If you have a Bean with the following * getters/setters for an indexed property: * * <pre> * public List getFoo() * public Object getFoo(int index) * public void setFoo(List foo) * public void setFoo(int index, Object foo) * </pre> * * then the IndexedPropertyDescriptor's getReadMethod() and getWriteMethod() * behave as follows: * <ul> * <li>JDK 1.3.1_04: returns valid Method objects from these methods.</li> * <li>JDK 1.4.2_05: returns null from these methods.</li> * </ul> * * @param beanClass the current class to be inspected * @param descriptors the array with property descriptors */
private void handleIndexedPropertyDescriptors(final Class<?> beanClass, final PropertyDescriptor[] descriptors) { for (final PropertyDescriptor pd : descriptors) { if (pd instanceof IndexedPropertyDescriptor) { final IndexedPropertyDescriptor descriptor = (IndexedPropertyDescriptor) pd; final String propName = descriptor.getName().substring(0, 1) .toUpperCase() + descriptor.getName().substring(1); if (descriptor.getReadMethod() == null) { final String methodName = descriptor.getIndexedReadMethod() != null ? descriptor .getIndexedReadMethod().getName() : "get" + propName; final Method readMethod = MethodUtils .getMatchingAccessibleMethod(beanClass, methodName, EMPTY_CLASS_PARAMETERS); if (readMethod != null) { try { descriptor.setReadMethod(readMethod); } catch (final Exception e) { log.error( "Error setting indexed property read method", e); } } } if (descriptor.getWriteMethod() == null) { final String methodName = descriptor.getIndexedWriteMethod() != null ? descriptor .getIndexedWriteMethod().getName() : "set" + propName; Method writeMethod = MethodUtils .getMatchingAccessibleMethod(beanClass, methodName, LIST_CLASS_PARAMETER); if (writeMethod == null) { for (final Method m : beanClass.getMethods()) { if (m.getName().equals(methodName)) { final Class<?>[] parameterTypes = m.getParameterTypes(); if (parameterTypes.length == 1 && List.class .isAssignableFrom(parameterTypes[0])) { writeMethod = m; break; } } } } if (writeMethod != null) { try { descriptor.setWriteMethod(writeMethod); } catch (final Exception e) { log.error( "Error setting indexed property write method", e); } } } } } } }