View Javadoc

1   package org.apache.commons.ognl.internal.entry;
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  /*
23   * $Id: PropertyDescriptorCacheEntryFactory.java 1430159 2013-01-08 07:40:04Z lukaszlenart $
24   */
25  
26  import org.apache.commons.ognl.ObjectIndexedPropertyDescriptor;
27  import org.apache.commons.ognl.OgnlException;
28  import org.apache.commons.ognl.OgnlRuntime;
29  import org.apache.commons.ognl.internal.CacheException;
30  
31  import java.beans.IntrospectionException;
32  import java.beans.Introspector;
33  import java.beans.PropertyDescriptor;
34  import java.lang.reflect.Method;
35  import java.util.ArrayList;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  
40  public class PropertyDescriptorCacheEntryFactory
41      implements ClassCacheEntryFactory<Map<String, PropertyDescriptor>>
42  {
43      public Map<String, PropertyDescriptor> create( Class<?> targetClass )
44          throws CacheException
45      {
46          Map<String, PropertyDescriptor> result = new HashMap<String, PropertyDescriptor>( 101 );
47          PropertyDescriptor[] pda;
48          try
49          {
50              pda = Introspector.getBeanInfo( targetClass ).getPropertyDescriptors();
51  
52              for (PropertyDescriptor aPda : pda) {
53                  // workaround for Introspector bug 6528714 (bugs.sun.com)
54                  if (aPda.getReadMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getReadMethod())) {
55                      aPda.setReadMethod(
56                              findClosestMatchingMethod(targetClass, aPda.getReadMethod(), aPda.getName(),
57                                      aPda.getPropertyType(), true));
58                  }
59                  if (aPda.getWriteMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getWriteMethod())) {
60                      aPda.setWriteMethod(
61                              findClosestMatchingMethod(targetClass, aPda.getWriteMethod(), aPda.getName(),
62                                      aPda.getPropertyType(), false));
63                  }
64  
65                  result.put(aPda.getName(), aPda);
66              }
67  
68              findObjectIndexedPropertyDescriptors( targetClass, result );
69          }
70          catch ( IntrospectionException e )
71          {
72              throw new CacheException( e );
73          }
74          catch ( OgnlException e )
75          {
76              throw new CacheException( e );
77          }
78          return result;
79      }
80  
81      static Method findClosestMatchingMethod( Class<?> targetClass, Method m, String propertyName, Class<?> propertyType,
82                                               boolean isReadMethod )
83          throws OgnlException
84      {
85          List<Method> methods = OgnlRuntime.getDeclaredMethods( targetClass, propertyName, !isReadMethod );
86  
87          for ( Method method : methods )
88          {
89              if ( method.getName().equals( m.getName() ) && m.getReturnType().isAssignableFrom( m.getReturnType() )
90                  && method.getReturnType() == propertyType
91                  && method.getParameterTypes().length == m.getParameterTypes().length )
92              {
93                  return method;
94              }
95          }
96  
97          return m;
98      }
99  
100     private static void findObjectIndexedPropertyDescriptors( Class<?> targetClass,
101                                                              Map<String, PropertyDescriptor> intoMap )
102         throws OgnlException
103     {
104         Map<String, List<Method>> allMethods = OgnlRuntime.getMethods( targetClass, false );
105         Map<String, List<Method>> pairs = new HashMap<String, List<Method>>( 101 );
106 
107         for ( Map.Entry<String, List<Method>> entry : allMethods.entrySet() )
108         {
109             String methodName = entry.getKey();
110             List<Method> methods = entry.getValue();
111 
112             /*
113              * Only process set/get where there is exactly one implementation of the method per class and those
114              * implementations are all the same
115              */
116             if ( indexMethodCheck( methods ) )
117             {
118                 boolean isGet = false, isSet;
119                 Method method = methods.get( 0 );
120 
121                 if ( ( ( isSet = methodName.startsWith( OgnlRuntime.SET_PREFIX ) ) || ( isGet =
122                     methodName.startsWith( OgnlRuntime.GET_PREFIX ) ) ) && ( methodName.length() > 3 ) )
123                 {
124                     String propertyName = Introspector.decapitalize( methodName.substring( 3 ) );
125                     Class<?>[] parameterTypes = OgnlRuntime.getParameterTypes( method );
126                     int parameterCount = parameterTypes.length;
127 
128                     if ( isGet && ( parameterCount == 1 ) && ( method.getReturnType() != Void.TYPE ) )
129                     {
130                         List<Method> pair = pairs.get( propertyName );
131 
132                         if ( pair == null )
133                         {
134                             pairs.put( propertyName, pair = new ArrayList<Method>() );
135                         }
136                         pair.add( method );
137                     }
138                     if ( isSet && ( parameterCount == 2 ) && ( method.getReturnType() == Void.TYPE ) )
139                     {
140                         List<Method> pair = pairs.get( propertyName );
141 
142                         if ( pair == null )
143                         {
144                             pairs.put( propertyName, pair = new ArrayList<Method>() );
145                         }
146                         pair.add( method );
147                     }
148                 }
149             }
150         }
151 
152         for ( Map.Entry<String, List<Method>> entry : pairs.entrySet() )
153         {
154             String propertyName = entry.getKey();
155             List<Method> methods = entry.getValue();
156 
157             if ( methods.size() == 2 )
158             {
159                 Method method1 = methods.get( 0 ), method2 = methods.get( 1 ), setMethod =
160                     ( method1.getParameterTypes().length == 2 ) ? method1 : method2, getMethod =
161                     ( setMethod == method1 ) ? method2 : method1;
162                 Class<?> keyType = getMethod.getParameterTypes()[0], propertyType = getMethod.getReturnType();
163 
164                 if ( keyType == setMethod.getParameterTypes()[0] )
165                 {
166                     if ( propertyType == setMethod.getParameterTypes()[1] )
167                     {
168                         ObjectIndexedPropertyDescriptor propertyDescriptor;
169 
170                         try
171                         {
172                             propertyDescriptor =
173                                 new ObjectIndexedPropertyDescriptor( propertyName, propertyType, getMethod, setMethod );
174                         }
175                         catch ( Exception ex )
176                         {
177                             throw new OgnlException(
178                                 "creating object indexed property descriptor for '" + propertyName + "' in "
179                                     + targetClass, ex );
180                         }
181                         intoMap.put( propertyName, propertyDescriptor );
182                     }
183                 }
184 
185             }
186         }
187     }
188     private static boolean indexMethodCheck( List<Method> methods )
189     {
190         boolean result = false;
191 
192         if ( methods.size() > 0 )
193         {
194             Method method = methods.get( 0 );
195             Class<?>[] parameterTypes = OgnlRuntime.getParameterTypes( method );
196             int numParameterTypes = parameterTypes.length;
197             Class<?> lastMethodClass = method.getDeclaringClass();
198 
199             result = true;
200             for ( int i = 1; result && ( i < methods.size() ); i++ )
201             {
202                 Class<?> clazz = methods.get( i ).getDeclaringClass();
203 
204                 // Check to see if more than one method implemented per class
205                 if ( lastMethodClass == clazz )
206                 {
207                     result = false;
208                 }
209                 else
210                 {
211                     Class<?>[] mpt = OgnlRuntime.getParameterTypes( method );
212                     int mpc = parameterTypes.length;
213 
214                     if ( numParameterTypes != mpc )
215                     {
216                         result = false;
217                     }
218                     for ( int j = 0; j < numParameterTypes; j++ )
219                     {
220                         if ( parameterTypes[j] != mpt[j] )
221                         {
222                             result = false;
223                             break;
224                         }
225                     }
226                 }
227                 lastMethodClass = clazz;
228             }
229         }
230         return result;
231     }
232 
233 
234 }