blob: 179e6d02dba2d843fa1cce886f0c252c67fde2f0 [file] [log] [blame]
package org.apache.commons.ognl.internal.entry;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* $Id$
*/
import org.apache.commons.ognl.ObjectIndexedPropertyDescriptor;
import org.apache.commons.ognl.OgnlException;
import org.apache.commons.ognl.OgnlRuntime;
import org.apache.commons.ognl.internal.CacheException;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PropertyDescriptorCacheEntryFactory
implements ClassCacheEntryFactory<Map<String, PropertyDescriptor>>
{
public Map<String, PropertyDescriptor> create( Class<?> targetClass )
throws CacheException
{
Map<String, PropertyDescriptor> result = new HashMap<String, PropertyDescriptor>( 101 );
PropertyDescriptor[] pda;
try
{
pda = Introspector.getBeanInfo( targetClass ).getPropertyDescriptors();
for (PropertyDescriptor aPda : pda) {
// workaround for Introspector bug 6528714 (bugs.sun.com)
if (aPda.getReadMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getReadMethod())) {
aPda.setReadMethod(
findClosestMatchingMethod(targetClass, aPda.getReadMethod(), aPda.getName(),
aPda.getPropertyType(), true));
}
if (aPda.getWriteMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getWriteMethod())) {
aPda.setWriteMethod(
findClosestMatchingMethod(targetClass, aPda.getWriteMethod(), aPda.getName(),
aPda.getPropertyType(), false));
}
result.put(aPda.getName(), aPda);
}
findObjectIndexedPropertyDescriptors( targetClass, result );
}
catch ( IntrospectionException e )
{
throw new CacheException( e );
}
catch ( OgnlException e )
{
throw new CacheException( e );
}
return result;
}
static Method findClosestMatchingMethod( Class<?> targetClass, Method m, String propertyName, Class<?> propertyType,
boolean isReadMethod )
throws OgnlException
{
List<Method> methods = OgnlRuntime.getDeclaredMethods( targetClass, propertyName, !isReadMethod );
for ( Method method : methods )
{
if ( method.getName().equals( m.getName() ) && m.getReturnType().isAssignableFrom( m.getReturnType() )
&& method.getReturnType() == propertyType
&& method.getParameterTypes().length == m.getParameterTypes().length )
{
return method;
}
}
return m;
}
private static void findObjectIndexedPropertyDescriptors( Class<?> targetClass,
Map<String, PropertyDescriptor> intoMap )
throws OgnlException
{
Map<String, List<Method>> allMethods = OgnlRuntime.getMethods( targetClass, false );
Map<String, List<Method>> pairs = new HashMap<String, List<Method>>( 101 );
for ( Map.Entry<String, List<Method>> entry : allMethods.entrySet() )
{
String methodName = entry.getKey();
List<Method> methods = entry.getValue();
/*
* Only process set/get where there is exactly one implementation of the method per class and those
* implementations are all the same
*/
if ( indexMethodCheck( methods ) )
{
boolean isGet = false, isSet;
Method method = methods.get( 0 );
if ( ( ( isSet = methodName.startsWith( OgnlRuntime.SET_PREFIX ) ) || ( isGet =
methodName.startsWith( OgnlRuntime.GET_PREFIX ) ) ) && ( methodName.length() > 3 ) )
{
String propertyName = Introspector.decapitalize( methodName.substring( 3 ) );
Class<?>[] parameterTypes = OgnlRuntime.getParameterTypes( method );
int parameterCount = parameterTypes.length;
if ( isGet && ( parameterCount == 1 ) && ( method.getReturnType() != Void.TYPE ) )
{
List<Method> pair = pairs.computeIfAbsent(propertyName, k -> new ArrayList<Method>());
pair.add( method );
}
if ( isSet && ( parameterCount == 2 ) && ( method.getReturnType() == Void.TYPE ) )
{
List<Method> pair = pairs.computeIfAbsent(propertyName, k -> new ArrayList<Method>());
pair.add( method );
}
}
}
}
for ( Map.Entry<String, List<Method>> entry : pairs.entrySet() )
{
String propertyName = entry.getKey();
List<Method> methods = entry.getValue();
if ( methods.size() == 2 )
{
Method method1 = methods.get( 0 ), method2 = methods.get( 1 ), setMethod =
( method1.getParameterTypes().length == 2 ) ? method1 : method2, getMethod =
( setMethod == method1 ) ? method2 : method1;
Class<?> keyType = getMethod.getParameterTypes()[0], propertyType = getMethod.getReturnType();
if ( keyType == setMethod.getParameterTypes()[0] && propertyType == setMethod.getParameterTypes()[1] )
{
ObjectIndexedPropertyDescriptor propertyDescriptor;
try
{
propertyDescriptor =
new ObjectIndexedPropertyDescriptor( propertyName, propertyType, getMethod, setMethod );
}
catch ( Exception ex )
{
throw new OgnlException(
"creating object indexed property descriptor for '" + propertyName + "' in "
+ targetClass, ex );
}
intoMap.put( propertyName, propertyDescriptor );
}
}
}
}
private static boolean indexMethodCheck( List<Method> methods )
{
boolean result = false;
if ( !methods.isEmpty() )
{
Method method = methods.get( 0 );
Class<?>[] parameterTypes = OgnlRuntime.getParameterTypes( method );
int numParameterTypes = parameterTypes.length;
Class<?> lastMethodClass = method.getDeclaringClass();
result = true;
for ( int i = 1; result && ( i < methods.size() ); i++ )
{
Class<?> clazz = methods.get( i ).getDeclaringClass();
// Check to see if more than one method implemented per class
if ( lastMethodClass == clazz )
{
result = false;
}
else
{
Class<?>[] mpt = OgnlRuntime.getParameterTypes( method );
int mpc = parameterTypes.length;
if ( numParameterTypes != mpc )
{
result = false;
}
for ( int j = 0; j < numParameterTypes; j++ )
{
if ( parameterTypes[j] != mpt[j] )
{
result = false;
break;
}
}
}
lastMethodClass = clazz;
}
}
return result;
}
}