BeanIntrospectionData.java

  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. import java.beans.IntrospectionException;
  19. import java.beans.PropertyDescriptor;
  20. import java.lang.reflect.Method;
  21. import java.util.HashMap;
  22. import java.util.Map;

  23. /**
  24.  * <p>
  25.  * An internally used helper class for storing introspection information about a bean class.
  26.  * </p>
  27.  * <p>
  28.  * This class is used by {@link PropertyUtilsBean}. When accessing bean properties via reflection information about the properties available and their types and
  29.  * access methods must be present. {@code PropertyUtilsBean} stores this information in a cache so that it can be accessed quickly. The cache stores instances
  30.  * of this class.
  31.  * </p>
  32.  * <p>
  33.  * This class mainly stores information about the properties of a bean class. Per default, this is contained in {@code PropertyDescriptor} objects. Some
  34.  * additional information required by the {@code BeanUtils} library is also stored here.
  35.  * </p>
  36.  *
  37.  * @since 1.9.1
  38.  */
  39. final class BeanIntrospectionData {
  40.     /**
  41.      * Initializes the map with the names of the write methods for the supported properties. The method names - if defined - need to be stored separately
  42.      * because they may get lost when the GC claims soft references used by the {@code PropertyDescriptor} objects.
  43.      *
  44.      * @param descs the array with the descriptors of the available properties
  45.      * @return the map with the names of write methods for properties
  46.      */
  47.     private static Map<String, String> setUpWriteMethodNames(final PropertyDescriptor[] descs) {
  48.         final Map<String, String> methods = new HashMap<>();
  49.         for (final PropertyDescriptor pd : descs) {
  50.             final Method method = pd.getWriteMethod();
  51.             if (method != null) {
  52.                 methods.put(pd.getName(), method.getName());
  53.             }
  54.         }
  55.         return methods;
  56.     }

  57.     /** An array with property descriptors for the managed bean class. */
  58.     private final PropertyDescriptor[] descriptors;

  59.     /** A map for remembering the write method names for properties. */
  60.     private final Map<String, String> writeMethodNames;

  61.     /**
  62.      * Creates a new instance of {@code BeanIntrospectionData} and initializes its completely.
  63.      *
  64.      * @param descs the array with the descriptors of the available properties
  65.      */
  66.     public BeanIntrospectionData(final PropertyDescriptor[] descs) {
  67.         this(descs, setUpWriteMethodNames(descs));
  68.     }

  69.     /**
  70.      * Creates a new instance of {@code BeanIntrospectionData} and allows setting the map with write method names. This constructor is mainly used for testing
  71.      * purposes.
  72.      *
  73.      * @param descs          the array with the descriptors of the available properties
  74.      * @param writeMethNames the map with the names of write methods
  75.      */
  76.     BeanIntrospectionData(final PropertyDescriptor[] descs, final Map<String, String> writeMethNames) {
  77.         descriptors = descs;
  78.         writeMethodNames = writeMethNames;
  79.     }

  80.     /**
  81.      * Returns the {@code PropertyDescriptor} for the property with the specified name. If this property is unknown, result is <strong>null</strong>.
  82.      *
  83.      * @param name the name of the property in question
  84.      * @return the {@code PropertyDescriptor} for this property or <strong>null</strong>
  85.      */
  86.     public PropertyDescriptor getDescriptor(final String name) {
  87.         for (final PropertyDescriptor pd : getDescriptors()) {
  88.             if (name.equals(pd.getName())) {
  89.                 return pd;
  90.             }
  91.         }
  92.         return null;
  93.     }

  94.     /**
  95.      * Returns the array with property descriptors.
  96.      *
  97.      * @return the property descriptors for the associated bean class
  98.      */
  99.     public PropertyDescriptor[] getDescriptors() {
  100.         return descriptors;
  101.     }

  102.     /**
  103.      * Returns the write method for the property determined by the given {@code PropertyDescriptor}. This information is normally available in the descriptor
  104.      * 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
  105.      * 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
  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 <strong>null</strong> 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, desc.getPropertyType());
  118.                 if (method != null) {
  119.                     try {
  120.                         desc.setWriteMethod(method);
  121.                     } catch (final IntrospectionException e) {
  122.                         // ignore, in this case the method is not cached
  123.                     }
  124.                 }
  125.             }
  126.         }

  127.         return method;
  128.     }
  129. }