DefaultBeanIntrospector.java
- /*
- * 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.beanutils2;
- 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;
- /**
- * <p>
- * The default {@link BeanIntrospector} implementation.
- * </p>
- * <p>
- * This class implements a default bean introspection algorithm based on the JDK classes in the {@link java.beans} 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>
- *
- * @since 1.9
- */
- public class DefaultBeanIntrospector implements BeanIntrospector {
- /** The singleton instance of this class. */
- public static final BeanIntrospector INSTANCE = new DefaultBeanIntrospector();
- /** 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 };
- /** For logging. Each subclass gets its own log instance. */
- private final Log log = LogFactory.getLog(getClass());
- /**
- * Private constructor so that no instances can be created.
- */
- private DefaultBeanIntrospector() {
- }
- /**
- * 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, BeanUtils.EMPTY_CLASS_ARRAY);
- 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);
- }
- }
- }
- }
- }
- }
- /**
- * 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
- */
- @Override
- 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 = PropertyDescriptors.EMPTY_ARRAY;
- }
- handleIndexedPropertyDescriptors(icontext.getTargetClass(), descriptors);
- icontext.addPropertyDescriptors(descriptors);
- }
- }