/* * 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; } }