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.beanutils; 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 28 * class. 29 * </p> 30 * <p> 31 * This class is used by {@link PropertyUtilsBean}. When accessing bean properties via 32 * reflection information about the properties available and their types and access 33 * methods must be present. {@code PropertyUtilsBean} stores this information in a cache 34 * so that it can be accessed quickly. The cache stores instances of this class. 35 * </p> 36 * <p> 37 * This class mainly stores information about the properties of a bean class. Per default, 38 * this is contained in {@code PropertyDescriptor} objects. Some additional information 39 * required by the {@code BeanUtils} library is also stored here. 40 * </p> 41 * 42 * @version $Id: BeanIntrospectionData.html 1048600 2019-08-14 01:36:40Z chtompki $ 43 * @since 1.9.1 44 */ 45 class BeanIntrospectionData { 46 /** An array with property descriptors for the managed bean class. */ 47 private final PropertyDescriptor[] descriptors; 48 49 /** A map for remembering the write method names for properties. */ 50 private final Map<String, String> writeMethodNames; 51 52 /** 53 * Creates a new instance of {@code BeanIntrospectionData} and initializes its 54 * completely. 55 * 56 * @param descs the array with the descriptors of the available properties 57 */ 58 public BeanIntrospectionData(final PropertyDescriptor[] descs) { 59 this(descs, setUpWriteMethodNames(descs)); 60 } 61 62 /** 63 * Creates a new instance of {@code BeanIntrospectionData} and allows setting the map 64 * with write method names. This constructor is mainly used for testing purposes. 65 * 66 * @param descs the array with the descriptors of the available properties 67 * @param writeMethNames the map with the names of write methods 68 */ 69 BeanIntrospectionData(final PropertyDescriptor[] descs, final Map<String, String> writeMethNames) { 70 descriptors = descs; 71 writeMethodNames = writeMethNames; 72 } 73 74 /** 75 * Returns the array with property descriptors. 76 * 77 * @return the property descriptors for the associated bean class 78 */ 79 public PropertyDescriptor[] getDescriptors() { 80 return descriptors; 81 } 82 83 /** 84 * Returns the {@code PropertyDescriptor} for the property with the specified name. If 85 * this property is unknown, result is <b>null</b>. 86 * 87 * @param name the name of the property in question 88 * @return the {@code PropertyDescriptor} for this property or <b>null</b> 89 */ 90 public PropertyDescriptor getDescriptor(final String name) { 91 for (final PropertyDescriptor pd : getDescriptors()) { 92 if (name.equals(pd.getName())) { 93 return pd; 94 } 95 } 96 return null; 97 } 98 99 /** 100 * Returns the write method for the property determined by the given 101 * {@code PropertyDescriptor}. This information is normally available in the 102 * descriptor object itself. However, at least by the ORACLE implementation, the 103 * method is stored as a {@code SoftReference}. If this reference has been freed by 104 * the GC, it may be the case that the method cannot be obtained again. Then, 105 * additional information stored in this object is necessary to obtain the method 106 * again. 107 * 108 * @param beanCls the class of the affected bean 109 * @param desc the {@code PropertyDescriptor} of the desired property 110 * @return the write method for this property or <b>null</b> if there is none 111 */ 112 public Method getWriteMethod(final Class<?> beanCls, final PropertyDescriptor desc) { 113 Method method = desc.getWriteMethod(); 114 if (method == null) { 115 final String methodName = writeMethodNames.get(desc.getName()); 116 if (methodName != null) { 117 method = MethodUtils.getAccessibleMethod(beanCls, methodName, 118 desc.getPropertyType()); 119 if (method != null) { 120 try { 121 desc.setWriteMethod(method); 122 } catch (final IntrospectionException e) { 123 // ignore, in this case the method is not cached 124 } 125 } 126 } 127 } 128 129 return method; 130 } 131 132 /** 133 * Initializes the map with the names of the write methods for the supported 134 * properties. The method names - if defined - need to be stored separately because 135 * they may get lost when the GC claims soft references used by the 136 * {@code PropertyDescriptor} objects. 137 * 138 * @param descs the array with the descriptors of the available properties 139 * @return the map with the names of write methods for properties 140 */ 141 private static Map<String, String> setUpWriteMethodNames(final PropertyDescriptor[] descs) { 142 final Map<String, String> methods = new HashMap<String, String>(); 143 for (final PropertyDescriptor pd : descs) { 144 final Method method = pd.getWriteMethod(); 145 if (method != null) { 146 methods.put(pd.getName(), method.getName()); 147 } 148 } 149 return methods; 150 } 151 }