001 package org.apache.commons.ognl.internal.entry;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 /*
023 * $Id: PropertyDescriptorCacheEntryFactory.java 1430159 2013-01-08 07:40:04Z lukaszlenart $
024 */
025
026 import org.apache.commons.ognl.ObjectIndexedPropertyDescriptor;
027 import org.apache.commons.ognl.OgnlException;
028 import org.apache.commons.ognl.OgnlRuntime;
029 import org.apache.commons.ognl.internal.CacheException;
030
031 import java.beans.IntrospectionException;
032 import java.beans.Introspector;
033 import java.beans.PropertyDescriptor;
034 import java.lang.reflect.Method;
035 import java.util.ArrayList;
036 import java.util.HashMap;
037 import java.util.List;
038 import java.util.Map;
039
040 public class PropertyDescriptorCacheEntryFactory
041 implements ClassCacheEntryFactory<Map<String, PropertyDescriptor>>
042 {
043 public Map<String, PropertyDescriptor> create( Class<?> targetClass )
044 throws CacheException
045 {
046 Map<String, PropertyDescriptor> result = new HashMap<String, PropertyDescriptor>( 101 );
047 PropertyDescriptor[] pda;
048 try
049 {
050 pda = Introspector.getBeanInfo( targetClass ).getPropertyDescriptors();
051
052 for (PropertyDescriptor aPda : pda) {
053 // workaround for Introspector bug 6528714 (bugs.sun.com)
054 if (aPda.getReadMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getReadMethod())) {
055 aPda.setReadMethod(
056 findClosestMatchingMethod(targetClass, aPda.getReadMethod(), aPda.getName(),
057 aPda.getPropertyType(), true));
058 }
059 if (aPda.getWriteMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getWriteMethod())) {
060 aPda.setWriteMethod(
061 findClosestMatchingMethod(targetClass, aPda.getWriteMethod(), aPda.getName(),
062 aPda.getPropertyType(), false));
063 }
064
065 result.put(aPda.getName(), aPda);
066 }
067
068 findObjectIndexedPropertyDescriptors( targetClass, result );
069 }
070 catch ( IntrospectionException e )
071 {
072 throw new CacheException( e );
073 }
074 catch ( OgnlException e )
075 {
076 throw new CacheException( e );
077 }
078 return result;
079 }
080
081 static Method findClosestMatchingMethod( Class<?> targetClass, Method m, String propertyName, Class<?> propertyType,
082 boolean isReadMethod )
083 throws OgnlException
084 {
085 List<Method> methods = OgnlRuntime.getDeclaredMethods( targetClass, propertyName, !isReadMethod );
086
087 for ( Method method : methods )
088 {
089 if ( method.getName().equals( m.getName() ) && m.getReturnType().isAssignableFrom( m.getReturnType() )
090 && method.getReturnType() == propertyType
091 && method.getParameterTypes().length == m.getParameterTypes().length )
092 {
093 return method;
094 }
095 }
096
097 return m;
098 }
099
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 }