blob: f499280613c5965fea2617f125795b5bcfabcf88 [file] [log] [blame]
/*
* 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.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* An internally used helper class for storing introspection information about a bean
* class.
* </p>
* <p>
* This class is used by {@link PropertyUtilsBean}. When accessing bean properties via
* reflection information about the properties available and their types and access
* methods must be present. {@code PropertyUtilsBean} stores this information in a cache
* so that it can be accessed quickly. The cache stores instances of this class.
* </p>
* <p>
* This class mainly stores information about the properties of a bean class. Per default,
* this is contained in {@code PropertyDescriptor} objects. Some additional information
* required by the {@code BeanUtils} library is also stored here.
* </p>
*
* @since 1.9.1
*/
class BeanIntrospectionData {
/** An array with property descriptors for the managed bean class. */
private final PropertyDescriptor[] descriptors;
/** A map for remembering the write method names for properties. */
private final Map<String, String> writeMethodNames;
/**
* Creates a new instance of {@code BeanIntrospectionData} and initializes its
* completely.
*
* @param descs the array with the descriptors of the available properties
*/
public BeanIntrospectionData(final PropertyDescriptor[] descs) {
this(descs, setUpWriteMethodNames(descs));
}
/**
* Creates a new instance of {@code BeanIntrospectionData} and allows setting the map
* with write method names. This constructor is mainly used for testing purposes.
*
* @param descs the array with the descriptors of the available properties
* @param writeMethNames the map with the names of write methods
*/
BeanIntrospectionData(final PropertyDescriptor[] descs, final Map<String, String> writeMethNames) {
descriptors = descs;
writeMethodNames = writeMethNames;
}
/**
* Returns the array with property descriptors.
*
* @return the property descriptors for the associated bean class
*/
public PropertyDescriptor[] getDescriptors() {
return descriptors;
}
/**
* Returns the {@code PropertyDescriptor} for the property with the specified name. If
* this property is unknown, result is <b>null</b>.
*
* @param name the name of the property in question
* @return the {@code PropertyDescriptor} for this property or <b>null</b>
*/
public PropertyDescriptor getDescriptor(final String name) {
for (final PropertyDescriptor pd : getDescriptors()) {
if (name.equals(pd.getName())) {
return pd;
}
}
return null;
}
/**
* Returns the write method for the property determined by the given
* {@code PropertyDescriptor}. This information is normally available in the
* descriptor object itself. However, at least by the ORACLE implementation, the
* method is stored as a {@code SoftReference}. If this reference has been freed by
* the GC, it may be the case that the method cannot be obtained again. Then,
* additional information stored in this object is necessary to obtain the method
* again.
*
* @param beanCls the class of the affected bean
* @param desc the {@code PropertyDescriptor} of the desired property
* @return the write method for this property or <b>null</b> if there is none
*/
public Method getWriteMethod(final Class<?> beanCls, final PropertyDescriptor desc) {
Method method = desc.getWriteMethod();
if (method == null) {
final String methodName = writeMethodNames.get(desc.getName());
if (methodName != null) {
method = MethodUtils.getAccessibleMethod(beanCls, methodName,
desc.getPropertyType());
if (method != null) {
try {
desc.setWriteMethod(method);
} catch (final IntrospectionException e) {
// ignore, in this case the method is not cached
}
}
}
}
return method;
}
/**
* Initializes the map with the names of the write methods for the supported
* properties. The method names - if defined - need to be stored separately because
* they may get lost when the GC claims soft references used by the
* {@code PropertyDescriptor} objects.
*
* @param descs the array with the descriptors of the available properties
* @return the map with the names of write methods for properties
*/
private static Map<String, String> setUpWriteMethodNames(final PropertyDescriptor[] descs) {
final Map<String, String> methods = new HashMap<>();
for (final PropertyDescriptor pd : descs) {
final Method method = pd.getWriteMethod();
if (method != null) {
methods.put(pd.getName(), method.getName());
}
}
return methods;
}
}