View Javadoc
1   package org.apache.commons.beanutils2;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static java.beans.Introspector.getBeanInfo;
23  
24  import java.beans.BeanInfo;
25  import java.beans.IndexedPropertyDescriptor;
26  import java.beans.IntrospectionException;
27  import java.beans.PropertyDescriptor;
28  import java.lang.reflect.Method;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Collection;
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.WeakHashMap;
36  import java.util.concurrent.locks.Lock;
37  import java.util.concurrent.locks.ReentrantLock;
38  
39  /**
40   *
41   */
42  final class PropertyDescriptorsRegistry
43  {
44  
45      private static final PropertyDescriptorsRegistry INSTANCE = new PropertyDescriptorsRegistry();
46  
47      public static PropertyDescriptorsRegistry getInstance()
48      {
49          return INSTANCE;
50      }
51  
52      private final AccessibleObjectsRegistry<Method> methodsRegistry = AccessibleObjectsRegistry.getMethodsRegistry();
53  
54      private final Map<Class<?>, Map<String, PropertyDescriptor>> cache =
55          new WeakHashMap<Class<?>, Map<String, PropertyDescriptor>>();
56  
57      /**
58       * This class can not be instantiated.
59       */
60      private PropertyDescriptorsRegistry()
61      {
62          // do nothing
63      }
64  
65      public Map<String, PropertyDescriptor> getPropertiesIndex( Class<?> beanType )
66          throws IntrospectionException
67      {
68          final Lock lock = new ReentrantLock();
69          lock.lock();
70          try
71          {
72              Map<String, PropertyDescriptor> propertiesIndex = cache.get( beanType );
73              if ( propertiesIndex != null )
74              {
75                  return propertiesIndex;
76              }
77  
78              propertiesIndex = new HashMap<String, PropertyDescriptor>();
79              BeanInfo beanInfo = getBeanInfo( beanType );
80  
81              List<PropertyDescriptor> propertyDescriptors =
82                  new ArrayList<PropertyDescriptor>( Arrays.asList( beanInfo.getPropertyDescriptors() ) );
83              propertyDescriptors.addAll( getMappedPropertyDescriptors( beanType ) );
84              for ( PropertyDescriptor propertyDescriptor : propertyDescriptors )
85              {
86                  makeMethodsAccessible( beanType, propertyDescriptor );
87                  propertiesIndex.put( propertyDescriptor.getName(), propertyDescriptor );
88              }
89  
90              cache.put( beanType, propertiesIndex );
91  
92              return propertiesIndex;
93          }
94          finally
95          {
96              lock.unlock();
97          }
98      }
99  
100     private Collection<PropertyDescriptor> getMappedPropertyDescriptors( Class<?> beanType )
101         throws IntrospectionException
102     {
103         Map<String, PropertyDescriptor> mappedPropertyDescirptors = new HashMap<String, PropertyDescriptor>();
104         for ( Method method : beanType.getMethods() )
105         {
106             if ( MethodUtils.isMappedGetter( method ) || MethodUtils.isMappedSetter( method ) )
107             {
108                 String propertyName = method.getName().substring( 3 );
109                 propertyName = Character.toLowerCase( propertyName.charAt( 0 ) ) + propertyName.substring( 1 );
110                 if ( !mappedPropertyDescirptors.containsKey( propertyName ) )
111                 {
112                     mappedPropertyDescirptors.put( propertyName, new MappedPropertyDescriptor( propertyName, beanType ) );
113                 }
114             }
115         }
116         return mappedPropertyDescirptors.values();
117     }
118 
119     private void makeMethodsAccessible( Class<?> beanType, PropertyDescriptor propertyDescriptor )
120         throws IntrospectionException
121     {
122         // we need to make sure that methods are accessible for anonymous types
123         // see https://issues.apache.org/jira/browse/BEANUTILS-157
124         if ( propertyDescriptor.getReadMethod() != null )
125         {
126             Method readMethod = propertyDescriptor.getReadMethod();
127             readMethod = methodsRegistry.get( true, beanType, readMethod.getName() );
128             propertyDescriptor.setReadMethod( readMethod );
129         }
130         if ( propertyDescriptor.getWriteMethod() != null )
131         {
132             Method writeMethod = propertyDescriptor.getWriteMethod();
133             writeMethod = methodsRegistry.get( true, beanType, writeMethod.getName(), writeMethod.getParameterTypes() );
134             propertyDescriptor.setWriteMethod( writeMethod );
135         }
136 
137         // indexed property
138 
139         if ( propertyDescriptor instanceof IndexedPropertyDescriptor )
140         {
141             IndexedPropertyDescriptor indexedPropertyDescriptor = (IndexedPropertyDescriptor) propertyDescriptor;
142 
143             if ( indexedPropertyDescriptor.getIndexedReadMethod() != null )
144             {
145                 Method indexedReadMethod = indexedPropertyDescriptor.getIndexedReadMethod();
146                 indexedReadMethod = methodsRegistry.get( true,
147                                                          beanType,
148                                                          indexedReadMethod.getName(),
149                                                          indexedReadMethod.getParameterTypes() );
150                 indexedPropertyDescriptor.setIndexedReadMethod( indexedReadMethod );
151             }
152             if ( indexedPropertyDescriptor.getIndexedWriteMethod() != null )
153             {
154                 Method indexedWriteMethod = indexedPropertyDescriptor.getIndexedWriteMethod();
155                 indexedWriteMethod = methodsRegistry.get( true,
156                                                           beanType,
157                                                           indexedWriteMethod.getName(),
158                                                           indexedWriteMethod.getParameterTypes() );
159                 indexedPropertyDescriptor.setIndexedWriteMethod( indexedWriteMethod );
160             }
161         }
162     }
163 
164     public PropertyDescriptor getPropertyDescriptor( Class<?> beanType, String propertyName )
165         throws IntrospectionException
166     {
167         Map<String, PropertyDescriptor> propertiesIndex = getPropertiesIndex( beanType );
168         return propertiesIndex.get( propertyName );
169     }
170 
171 }