Author: simonetripodi Date: Sun Jan 29 16:16:51 2012 New Revision: 1237323 URL: http://svn.apache.org/viewvc?rev=1237323&view=rev Log: centralized all the logic in the abstract implementation
Modified: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/AccessibleObjectsRegistry.java Modified: commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/AccessibleObjectsRegistry.java URL: http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/AccessibleObjectsRegistry.java?rev=1237323&r1=1237322&r2=1237323&view=diff ============================================================================== --- commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/AccessibleObjectsRegistry.java (original) +++ commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/AccessibleObjectsRegistry.java Sun Jan 29 16:16:51 2012 @@ -28,8 +28,8 @@ import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; +import java.lang.reflect.Member; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Map; @@ -37,7 +37,7 @@ import java.util.WeakHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -abstract class AccessibleObjectsRegistry<AO extends AccessibleObject> +abstract class AccessibleObjectsRegistry<AO extends AccessibleObject & Member> { private static final AccessibleObjectsRegistry<Constructor<?>> CONSTRUCTORS_REGISTRY = new ConstructorsRegistry(); @@ -67,27 +67,89 @@ abstract class AccessibleObjectsRegistry return get( exact, type, null, parameterTypes ); } - public final AO get( boolean exact, Class<?> type, String methodName, Class<?>... parameterTypes ) + public final AO get( boolean exact, Class<?> type, String name, Class<?>... parameterTypes ) { - AccessibleObjectDescriptor key = new AccessibleObjectDescriptor( exact, type, methodName, parameterTypes ); + AccessibleObjectDescriptor key = new AccessibleObjectDescriptor( exact, type, name, parameterTypes ); final Lock lock = new ReentrantLock(); lock.lock(); try { - Reference<AO> methodReference = cache.get( key ); - if ( methodReference != null ) + Reference<AO> accessibleObjectReference = cache.get( key ); + if ( accessibleObjectReference != null ) { - return methodReference.get(); + return accessibleObjectReference.get(); } - AO accessibleObject = resolve( exact, type, methodName, parameterTypes ); - if ( accessibleObject != null ) + // see if we can find the accessible object directly + // most of the time this works and it's much faster + try { - accessibleObject = makeAccessible( accessibleObject ); + AO accessibleObject = resolveDirectly( type, name, parameterTypes ); + if ( exact || accessibleObject != null ) + { + return makeAccessible( accessibleObject ); + } } - cache.put( key, new WeakReference<AO>( accessibleObject ) ); - return accessibleObject; + catch ( NoSuchMethodException e ) + { + /* SWALLOW */ + if ( exact ) + { + return null; + } + } + + // start calculating the best matching + + int paramSize = parameterTypes.length; + AO bestMatch = null; + AO[] accessibleObjectsArray = getAccessibleObjectsArray( type ); + float bestMatchCost = Float.MAX_VALUE; + float myCost = Float.MAX_VALUE; + for ( int i = 0, size = accessibleObjectsArray.length; i < size; i++ ) + { + if ( matches( accessibleObjectsArray[i], name ) ) + { + // compare parameters + Class<?>[] methodsParams = getParameterTypes( accessibleObjectsArray[i] ); + int methodParamSize = methodsParams.length; + if ( methodParamSize == paramSize ) + { + boolean match = true; + for ( int n = 0; n < methodParamSize; n++ ) + { + if ( !isAssignmentCompatible( methodsParams[n], parameterTypes[n] ) ) + { + match = false; + break; + } + } + + if ( match ) + { + // get accessible version of method + AO current = resolveAccessible( type, accessibleObjectsArray[i] ); + if ( current != null ) + { + myCost = getTotalTransformationCost( parameterTypes, getParameterTypes( current ) ); + if ( myCost < bestMatchCost ) + { + bestMatch = current; + bestMatchCost = myCost; + } + } + } + } + } + } + + if ( bestMatch != null ) + { + bestMatch = makeAccessible( bestMatch ); + } + cache.put( key, new WeakReference<AO>( bestMatch ) ); + return bestMatch; } finally { @@ -95,6 +157,85 @@ abstract class AccessibleObjectsRegistry } } + protected abstract AO resolveDirectly( Class<?> type, String name, Class<?>... parameterTypes ) + throws NoSuchMethodException; + + protected abstract AO[] getAccessibleObjectsArray( Class<?> type ); + + protected abstract boolean matches( AO accessibleObject, String name ); + + protected abstract Class<?>[] getParameterTypes( AO accessibleObject ); + + protected abstract AO resolveAccessible( Class<?> type, AO accessibleObject ); + + /** + * Returns the sum of the object transformation cost for each class in the source + * argument list. + * @param srcArgs The source arguments + * @param destArgs The destination arguments + * @return The total transformation cost + */ + private static float getTotalTransformationCost( Class<?>[] srcArgs, Class<?>[] destArgs ) + { + float totalCost = 0.0f; + for ( int i = 0; i < srcArgs.length; i++ ) + { + Class<?> srcClass, destClass; + srcClass = srcArgs[i]; + destClass = destArgs[i]; + totalCost += getObjectTransformationCost( srcClass, destClass ); + } + return totalCost; + } + + /** + * Gets the number of steps required needed to turn the source class into the + * destination class. This represents the number of steps in the object hierarchy + * graph. + * + * @param srcClass The source class + * @param destClass The destination class + * @return The cost of transforming an object + */ + private static float getObjectTransformationCost( Class<?> srcClass, Class<?> destClass ) + { + float cost = 0.0f; + while ( srcClass != null && !destClass.equals( srcClass ) ) + { + if ( destClass.isPrimitive() ) + { + Class<?> destClassWrapperClazz = getPrimitiveWrapper( destClass ); + if ( destClassWrapperClazz != null && destClassWrapperClazz.equals( srcClass ) ) + { + cost += 0.25f; + break; + } + } + if ( destClass.isInterface() && isAssignmentCompatible( destClass, srcClass ) ) + { + // slight penalty for interface match. + // we still want an exact match to override an interface match, but + // an interface match should override anything where we have to get a + // superclass. + cost += 0.25f; + break; + } + cost++; + srcClass = srcClass.getSuperclass(); + } + + /* + * If the destination class is null, we've travelled all the way up to an Object match. We'll penalize this + * by adding 1.5 to the cost. + */ + if ( srcClass == null ) + { + cost += 1.5f; + } + + return cost; + } + private AO makeAccessible( AO accessibleObject ) { PrivilegedAction<AO> action = new MakeAccessiblePrivilegedAction<AO>( accessibleObject ); @@ -105,8 +246,6 @@ abstract class AccessibleObjectsRegistry return action.run(); } - protected abstract AO resolve( boolean exact, Class<?> cls, String methodName, Class<?>... paramTypes ); - /** * Constructors registry implementation. */ @@ -114,87 +253,48 @@ abstract class AccessibleObjectsRegistry { @Override - protected Constructor<?> resolve( boolean exact, Class<?> type, String methodName, Class<?>...parameterTypes ) + protected Constructor<?> resolveDirectly( Class<?> type, String name, Class<?>...parameterTypes ) + throws NoSuchMethodException { - // see if we can find the method directly - // most of the time this works and it's much faster - try - { - Constructor<?> ctor = type.getConstructor( parameterTypes ); - if ( exact || ctor != null ) - { - return getAccessibleConstructor( ctor ); - } - } - catch ( NoSuchMethodException e ) - { - /* SWALLOW */ - if ( exact ) - { - return null; - } - } - - // search through all methods - int paramSize = parameterTypes.length; + return type.getConstructor( parameterTypes ); + } - for ( Constructor<?> ctor : type.getConstructors() ) - { - // compare parameters - Class<?>[] ctorParams = ctor.getParameterTypes(); - int ctorParamSize = ctorParams.length; - if ( ctorParamSize == paramSize ) - { - boolean match = true; - for ( int n = 0; n < ctorParamSize; n++ ) - { - if ( !isAssignmentCompatible( ctorParams[n], parameterTypes[n] ) ) - { - match = false; - break; - } - } + @Override + protected Constructor<?>[] getAccessibleObjectsArray( Class<?> type ) + { + return type.getConstructors(); + } - if ( match ) - { - // get accessible version of method - ctor = getAccessibleConstructor( ctor ); - if ( ctor != null ) - { - return ctor; - } - } - } - } + @Override + protected boolean matches( Constructor<?> accessibleObject, String name ) + { + return true; + } - return null; + @Override + protected Class<?>[] getParameterTypes( Constructor<?> accessibleObject ) + { + return accessibleObject.getParameterTypes(); } - /** - * Returns accessible version of the given constructor. - * @param ctor prototype constructor object. - * @return <code>null</code> if accessible constructor can not be found. - * @see java.lang.SecurityManager - */ - private static <T> Constructor<T> getAccessibleConstructor( Constructor<T> ctor ) + @Override + protected Constructor<?> resolveAccessible( Class<?> type, Constructor<?> accessibleObject ) { - // Make sure we have a method to check - if ( ctor == null ) + if ( accessibleObject == null ) { - return ( null ); + return null; } - // If the requested method is not public we cannot call it - if ( !isPublic( ctor.getModifiers() ) ) + if ( !isPublic( accessibleObject.getModifiers() ) ) { - return ( null ); + return null; } // If the declaring class is public, we are done - Class<T> beanClass = ctor.getDeclaringClass(); + Class<?> beanClass = accessibleObject.getDeclaringClass(); if ( isPublic( beanClass.getModifiers() ) ) { - return ctor; + return accessibleObject; } // what else can we do? @@ -210,108 +310,57 @@ abstract class AccessibleObjectsRegistry { @Override - public Method resolve( boolean exact, Class<?> clazz, String methodName, Class<?>...parameterTypes ) + protected Method resolveDirectly( Class<?> type, String name, Class<?>...parameterTypes ) + throws NoSuchMethodException { - // see if we can find the method directly - // most of the time this works and it's much faster - try - { - Method method = clazz.getMethod(methodName, parameterTypes); - if ( exact || method != null ) - { - return method; - } - } - catch ( NoSuchMethodException e ) - { - /* SWALLOW */ - if ( exact ) - { - return null; - } - } - - // search through all methods - int paramSize = parameterTypes.length; - Method bestMatch = null; - Method[] methods = clazz.getMethods(); - float bestMatchCost = Float.MAX_VALUE; - float myCost = Float.MAX_VALUE; - for ( int i = 0, size = methods.length; i < size; i++ ) - { - if ( methods[i].getName().equals( methodName ) ) - { - // compare parameters - Class<?>[] methodsParams = methods[i].getParameterTypes(); - int methodParamSize = methodsParams.length; - if ( methodParamSize == paramSize ) - { - boolean match = true; - for ( int n = 0; n < methodParamSize; n++ ) - { + return type.getMethod( name, parameterTypes ); + } - if ( !isAssignmentCompatible( methodsParams[n], parameterTypes[n] ) ) - { - match = false; - break; - } - } + @Override + protected Method[] getAccessibleObjectsArray( Class<?> type ) + { + return type.getMethods(); + } - if ( match ) - { - // get accessible version of method - Method method = getAccessibleMethod( clazz, methods[i] ); - if ( method != null ) - { - myCost = getTotalTransformationCost( parameterTypes, method.getParameterTypes() ); - if ( myCost < bestMatchCost ) - { - bestMatch = method; - bestMatchCost = myCost; - } - } - } - } - } - } + @Override + protected boolean matches( Method accessibleObject, String name ) + { + return name.equals( accessibleObject.getName() ); + } - return bestMatch; + @Override + protected Class<?>[] getParameterTypes( Method accessibleObject ) + { + return accessibleObject.getParameterTypes(); } - /** - * <p>Return an accessible method (that is, one that can be invoked via - * reflection) that implements the specified Method. If no such method - * can be found, return <code>null</code>.</p> - * - * @param clazz The class of the object - * @param method The method that we wish to call - * @return The accessible method - */ - private static Method getAccessibleMethod(Class<?> clazz, Method method) { + @Override + protected Method resolveAccessible( Class<?> type, Method method ) + { // Make sure we have a method to check if ( method == null ) { - return ( null ); + return null; } // If the requested method is not public we cannot call it - if ( !Modifier.isPublic( method.getModifiers() ) ) + if ( !isPublic( method.getModifiers() ) ) { - return ( null ); + return null; } - if ( clazz == null ) + if ( type == null ) { - clazz = method.getDeclaringClass(); + type = method.getDeclaringClass(); } else { - checkArgument( method.getDeclaringClass().isAssignableFrom( clazz ), - "%s is not assignable from ", clazz.getName(), method.getDeclaringClass().getName() ); + checkArgument( method.getDeclaringClass().isAssignableFrom( type ), + "%s is not assignable from ", type.getName(), method.getDeclaringClass().getName() ); } // If the class is public, we are done - if ( isPublic( clazz.getModifiers() ) ) + if ( isPublic( type.getModifiers() ) ) { return method; } @@ -320,86 +369,18 @@ abstract class AccessibleObjectsRegistry Class<?>[] parameterTypes = method.getParameterTypes(); // Check the implemented interfaces and subinterfaces - method = getAccessibleMethodFromInterfaceNest( clazz, methodName, parameterTypes ); + method = getAccessibleMethodFromInterfaceNest( type, methodName, parameterTypes ); // Check the superclass chain if ( method == null ) { - method = getAccessibleMethodFromSuperclass( clazz, methodName, parameterTypes ); + method = getAccessibleMethodFromSuperclass( type, methodName, parameterTypes ); } return method; } /** - * Returns the sum of the object transformation cost for each class in the source - * argument list. - * @param srcArgs The source arguments - * @param destArgs The destination arguments - * @return The total transformation cost - */ - private static float getTotalTransformationCost( Class<?>[] srcArgs, Class<?>[] destArgs ) - { - float totalCost = 0.0f; - for ( int i = 0; i < srcArgs.length; i++ ) - { - Class<?> srcClass, destClass; - srcClass = srcArgs[i]; - destClass = destArgs[i]; - totalCost += getObjectTransformationCost( srcClass, destClass ); - } - return totalCost; - } - - /** - * Gets the number of steps required needed to turn the source class into the - * destination class. This represents the number of steps in the object hierarchy - * graph. - * - * @param srcClass The source class - * @param destClass The destination class - * @return The cost of transforming an object - */ - private static float getObjectTransformationCost( Class<?> srcClass, Class<?> destClass ) - { - float cost = 0.0f; - while ( srcClass != null && !destClass.equals( srcClass ) ) - { - if ( destClass.isPrimitive() ) - { - Class<?> destClassWrapperClazz = getPrimitiveWrapper( destClass ); - if ( destClassWrapperClazz != null && destClassWrapperClazz.equals( srcClass ) ) - { - cost += 0.25f; - break; - } - } - if ( destClass.isInterface() && isAssignmentCompatible( destClass, srcClass ) ) - { - // slight penalty for interface match. - // we still want an exact match to override an interface match, but - // an interface match should override anything where we have to get a - // superclass. - cost += 0.25f; - break; - } - cost++; - srcClass = srcClass.getSuperclass(); - } - - /* - * If the destination class is null, we've travelled all the way up to an Object match. We'll penalize this - * by adding 1.5 to the cost. - */ - if ( srcClass == null ) - { - cost += 1.5f; - } - - return cost; - } - - /** * <p>Return an accessible method (that is, one that can be invoked via * reflection) by scanning through the superclasses. If no such method * can be found, return <code>null</code>.</p>