1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.beanutils2; 18 19 import java.beans.IntrospectionException; 20 import java.beans.PropertyDescriptor; 21 import java.lang.reflect.Method; 22 import java.util.HashMap; 23 import java.util.Map; 24 25 /** 26 * <p> 27 * An internally used helper class for storing introspection information about a bean class. 28 * </p> 29 * <p> 30 * This class is used by {@link PropertyUtilsBean}. When accessing bean properties via reflection information about the properties available and their types and 31 * access methods must be present. {@code PropertyUtilsBean} stores this information in a cache so that it can be accessed quickly. The cache stores instances 32 * of this class. 33 * </p> 34 * <p> 35 * This class mainly stores information about the properties of a bean class. Per default, this is contained in {@code PropertyDescriptor} objects. Some 36 * additional information required by the {@code BeanUtils} library is also stored here. 37 * </p> 38 * 39 * @since 1.9.1 40 */ 41 final class BeanIntrospectionData { 42 /** 43 * Initializes the map with the names of the write methods for the supported properties. The method names - if defined - need to be stored separately 44 * because they may get lost when the GC claims soft references used by the {@code PropertyDescriptor} objects. 45 * 46 * @param descs the array with the descriptors of the available properties 47 * @return the map with the names of write methods for properties 48 */ 49 private static Map<String, String> setUpWriteMethodNames(final PropertyDescriptor[] descs) { 50 final Map<String, String> methods = new HashMap<>(); 51 for (final PropertyDescriptor pd : descs) { 52 final Method method = pd.getWriteMethod(); 53 if (method != null) { 54 methods.put(pd.getName(), method.getName()); 55 } 56 } 57 return methods; 58 } 59 60 /** An array with property descriptors for the managed bean class. */ 61 private final PropertyDescriptor[] descriptors; 62 63 /** A map for remembering the write method names for properties. */ 64 private final Map<String, String> writeMethodNames; 65 66 /** 67 * Creates a new instance of {@code BeanIntrospectionData} and initializes its completely. 68 * 69 * @param descs the array with the descriptors of the available properties 70 */ 71 public BeanIntrospectionData(final PropertyDescriptor[] descs) { 72 this(descs, setUpWriteMethodNames(descs)); 73 } 74 75 /** 76 * Creates a new instance of {@code BeanIntrospectionData} and allows setting the map with write method names. This constructor is mainly used for testing 77 * purposes. 78 * 79 * @param descs the array with the descriptors of the available properties 80 * @param writeMethNames the map with the names of write methods 81 */ 82 BeanIntrospectionData(final PropertyDescriptor[] descs, final Map<String, String> writeMethNames) { 83 descriptors = descs; 84 writeMethodNames = writeMethNames; 85 } 86 87 /** 88 * Returns the {@code PropertyDescriptor} for the property with the specified name. If this property is unknown, result is <strong>null</strong>. 89 * 90 * @param name the name of the property in question 91 * @return the {@code PropertyDescriptor} for this property or <strong>null</strong> 92 */ 93 public PropertyDescriptor getDescriptor(final String name) { 94 for (final PropertyDescriptor pd : getDescriptors()) { 95 if (name.equals(pd.getName())) { 96 return pd; 97 } 98 } 99 return null; 100 } 101 102 /** 103 * Returns the array with property descriptors. 104 * 105 * @return the property descriptors for the associated bean class 106 */ 107 public PropertyDescriptor[] getDescriptors() { 108 return descriptors; 109 } 110 111 /** 112 * Returns the write method for the property determined by the given {@code PropertyDescriptor}. This information is normally available in the descriptor 113 * 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 114 * 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 115 * again. 116 * 117 * @param beanCls the class of the affected bean 118 * @param desc the {@code PropertyDescriptor} of the desired property 119 * @return the write method for this property or <strong>null</strong> if there is none 120 */ 121 public Method getWriteMethod(final Class<?> beanCls, final PropertyDescriptor desc) { 122 Method method = desc.getWriteMethod(); 123 if (method == null) { 124 final String methodName = writeMethodNames.get(desc.getName()); 125 if (methodName != null) { 126 method = MethodUtils.getAccessibleMethod(beanCls, methodName, desc.getPropertyType()); 127 if (method != null) { 128 try { 129 desc.setWriteMethod(method); 130 } catch (final IntrospectionException e) { 131 // ignore, in this case the method is not cached 132 } 133 } 134 } 135 } 136 137 return method; 138 } 139 }