package org.codehaus.plexus.interpolation.reflection;

/* ====================================================================
 *   Copyright 2001-2004 The Apache Software Foundation.
 *
 *   Licensed 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.
 * ====================================================================
 */

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Hashtable;
import java.util.Map;

NOTE: This class was copied from plexus-utils, to allow this library to stand completely self-contained.

A cache of introspection information for a specific class instance. Keys Method objects by a concatenation of the method name and the names of classes that make up the parameters.

Author:Jason van Zyl, Bob McWhirter, Attila Szegedi, Geir Magnusson Jr.
/** * <b>NOTE:</b> This class was copied from plexus-utils, to allow this library * to stand completely self-contained. * <p>A cache of introspection information for a specific class instance. * Keys {@link Method} objects by a concatenation of the * method name and the names of classes that make up the parameters.</p> * * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> * @author <a href="mailto:bob@werken.com">Bob McWhirter</a> * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a> * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> */
public class ClassMap { private static final class CacheMiss { } private static final CacheMiss CACHE_MISS = new CacheMiss(); private static final Object OBJECT = new Object();
Class passed into the constructor used to as the basis for the Method map.
/** * Class passed into the constructor used to as * the basis for the Method map. */
private Class<?> clazz;
Cache of Methods, or CACHE_MISS, keyed by method name and actual arguments used to find it.
/** * Cache of Methods, or CACHE_MISS, keyed by method * name and actual arguments used to find it. */
private Map<String, Object> methodCache = new Hashtable<String, Object>(); private MethodMap methodMap = new MethodMap();
Standard constructor
Params:
  • clazz – The class to be analyzed.
/** * Standard constructor * @param clazz The class to be analyzed. */
public ClassMap( Class<?> clazz ) { this.clazz = clazz; populateMethodCache(); }
Returns:the class object whose methods are cached by this map.
/** * @return the class object whose methods are cached by this map. */
Class<?> getCachedClass() { return clazz; }

Find a Method using the methodKey provided.

Look in the methodMap for an entry. If found, it'll either be a CACHE_MISS, in which case we simply give up, or it'll be a Method, in which case, we return it.

If nothing is found, then we must actually go and introspect the method from the MethodMap.

Params:
  • name – name of the method.
  • params – The parameters for the method.
Throws:
Returns:Method.
/** * <p>Find a Method using the methodKey * provided.</p> * <p>Look in the methodMap for an entry. If found, * it'll either be a CACHE_MISS, in which case we * simply give up, or it'll be a Method, in which * case, we return it.</p> * <p>If nothing is found, then we must actually go * and introspect the method from the MethodMap.</p> * @param name name of the method. * @param params The parameters for the method. * @return {@link Method}. * @throws MethodMap.AmbiguousException in case of an error. */
public Method findMethod( String name, Object[] params ) throws MethodMap.AmbiguousException { String methodKey = makeMethodKey( name, params ); Object cacheEntry = methodCache.get( methodKey ); if ( cacheEntry == CACHE_MISS ) { return null; } if ( cacheEntry == null ) { try { cacheEntry = methodMap.find( name, params ); } catch ( MethodMap.AmbiguousException ae ) { /* * that's a miss :) */ methodCache.put( methodKey, CACHE_MISS ); throw ae; } if ( cacheEntry == null ) { methodCache.put( methodKey, CACHE_MISS ); } else { methodCache.put( methodKey, cacheEntry ); } } // Yes, this might just be null. return (Method) cacheEntry; }
Populate the Map of direct hits. These are taken from all the public methods that our class provides.
/** * Populate the Map of direct hits. These * are taken from all the public methods * that our class provides. */
private void populateMethodCache() { /* * get all publicly accessible methods */ Method[] methods = getAccessibleMethods( clazz ); /* * map and cache them */ for ( Method method : methods ) { /* * now get the 'public method', the method declared by a * public interface or class. (because the actual implementing * class may be a facade... */ Method publicMethod = getPublicMethod( method ); /* * it is entirely possible that there is no public method for * the methods of this class (i.e. in the facade, a method * that isn't on any of the interfaces or superclass * in which case, ignore it. Otherwise, map and cache */ if ( publicMethod != null ) { methodMap.add( publicMethod ); methodCache.put( makeMethodKey( publicMethod ), publicMethod ); } } }
Make a methodKey for the given method using the concatenation of the name and the types of the method parameters.
/** * Make a methodKey for the given method using * the concatenation of the name and the * types of the method parameters. */
private String makeMethodKey( Method method ) { Class<?>[] parameterTypes = method.getParameterTypes(); StringBuilder methodKey = new StringBuilder( method.getName() ); for ( Class<?> parameterType : parameterTypes ) { /* * If the argument type is primitive then we want * to convert our primitive type signature to the * corresponding Object type so introspection for * methods with primitive types will work correctly. */ if ( parameterType.isPrimitive() ) { if ( parameterType.equals( Boolean.TYPE ) ) { methodKey.append( "java.lang.Boolean" ); } else if ( parameterType.equals( Byte.TYPE ) ) { methodKey.append( "java.lang.Byte" ); } else if ( parameterType.equals( Character.TYPE ) ) { methodKey.append( "java.lang.Character" ); } else if ( parameterType.equals( Double.TYPE ) ) { methodKey.append( "java.lang.Double" ); } else if ( parameterType.equals( Float.TYPE ) ) { methodKey.append( "java.lang.Float" ); } else if ( parameterType.equals( Integer.TYPE ) ) { methodKey.append( "java.lang.Integer" ); } else if ( parameterType.equals( Long.TYPE ) ) { methodKey.append( "java.lang.Long" ); } else if ( parameterType.equals( Short.TYPE ) ) { methodKey.append( "java.lang.Short" ); } } else { methodKey.append( parameterType.getName() ); } } return methodKey.toString(); } private static String makeMethodKey( String method, Object[] params ) { if (params.length == 0) { return method; } StringBuilder methodKey = new StringBuilder().append( method ); for ( Object arg : params ) { if ( arg == null ) { arg = OBJECT; } methodKey.append( arg.getClass().getName() ); } return methodKey.toString(); }
Retrieves public methods for a class. In case the class is not public, retrieves methods with same signature as its public methods from public superclasses and interfaces (if they exist). Basically upcasts every method to the nearest acccessible method.
/** * Retrieves public methods for a class. In case the class is not * public, retrieves methods with same signature as its public methods * from public superclasses and interfaces (if they exist). Basically * upcasts every method to the nearest acccessible method. */
private static Method[] getAccessibleMethods( Class<?> clazz ) { Method[] methods = clazz.getMethods(); /* * Short circuit for the (hopefully) majority of cases where the * clazz is public */ if ( Modifier.isPublic( clazz.getModifiers() ) ) { return methods; } /* * No luck - the class is not public, so we're going the longer way. */ MethodInfo[] methodInfos = new MethodInfo[methods.length]; for ( int i = methods.length; i-- > 0; ) { methodInfos[i] = new MethodInfo( methods[i] ); } int upcastCount = getAccessibleMethods( clazz, methodInfos, 0 ); /* * Reallocate array in case some method had no accessible counterpart. */ if ( upcastCount < methods.length ) { methods = new Method[upcastCount]; } int j = 0; for ( MethodInfo methodInfo : methodInfos ) { if ( methodInfo.upcast ) { methods[j++] = methodInfo.method; } } return methods; }
Recursively finds a match for each method, starting with the class, and then searching the superclass and interfaces.
Params:
  • clazz – Class to check
  • methodInfos – array of methods we are searching to match
  • upcastCount – current number of methods we have matched
Returns:count of matched methods
/** * Recursively finds a match for each method, starting with the class, and then * searching the superclass and interfaces. * * @param clazz Class to check * @param methodInfos array of methods we are searching to match * @param upcastCount current number of methods we have matched * @return count of matched methods */
private static int getAccessibleMethods( Class<?> clazz, MethodInfo[] methodInfos, int upcastCount ) { int l = methodInfos.length; /* * if this class is public, then check each of the currently * 'non-upcasted' methods to see if we have a match */ if ( Modifier.isPublic( clazz.getModifiers() ) ) { for ( int i = 0; i < l && upcastCount < l; ++i ) { try { MethodInfo methodInfo = methodInfos[i]; if ( !methodInfo.upcast ) { methodInfo.tryUpcasting( clazz ); upcastCount++; } } catch ( NoSuchMethodException e ) { /* * Intentionally ignored - it means * it wasn't found in the current class */ } } /* * Short circuit if all methods were upcast */ if ( upcastCount == l ) { return upcastCount; } } /* * Examine superclass */ Class<?> superclazz = clazz.getSuperclass(); if ( superclazz != null ) { upcastCount = getAccessibleMethods( superclazz, methodInfos, upcastCount ); /* * Short circuit if all methods were upcast */ if ( upcastCount == l ) { return upcastCount; } } /* * Examine interfaces. Note we do it even if superclazz == null. * This is redundant as currently java.lang.Object does not implement * any interfaces, however nothing guarantees it will not in future. */ Class<?>[] interfaces = clazz.getInterfaces(); for ( int i = interfaces.length; i-- > 0; ) { upcastCount = getAccessibleMethods( interfaces[i], methodInfos, upcastCount ); /* * Short circuit if all methods were upcast */ if ( upcastCount == l ) { return upcastCount; } } return upcastCount; }
For a given method, retrieves its publicly accessible counterpart. This method will look for a method with same name and signature declared in a public superclass or implemented interface of this method's declaring class. This counterpart method is publicly callable.
Params:
  • method – a method whose publicly callable counterpart is requested.
Returns:the publicly callable counterpart method. Note that if the parameter method is itself declared by a public class, this method is an identity function.
/** * For a given method, retrieves its publicly accessible counterpart. * This method will look for a method with same name * and signature declared in a public superclass or implemented interface of this * method's declaring class. This counterpart method is publicly callable. * * @param method a method whose publicly callable counterpart is requested. * @return the publicly callable counterpart method. Note that if the parameter * method is itself declared by a public class, this method is an identity * function. */
public static Method getPublicMethod( Method method ) { Class<?> clazz = method.getDeclaringClass(); /* * Short circuit for (hopefully the majority of) cases where the declaring * class is public. */ if ( ( clazz.getModifiers() & Modifier.PUBLIC ) != 0 ) { return method; } return getPublicMethod( clazz, method.getName(), method.getParameterTypes() ); }
Looks up the method with specified name and signature in the first public superclass or implemented interface of the class.
Params:
  • name – the name of the method
  • paramTypes – the classes of method parameters
@paramclass the class whose method is sought
/** * Looks up the method with specified name and signature in the first public * superclass or implemented interface of the class. * * @param class the class whose method is sought * @param name the name of the method * @param paramTypes the classes of method parameters */
private static Method getPublicMethod( Class<?> clazz, String name, Class<?>[] paramTypes ) { /* * if this class is public, then try to get it */ if ( ( clazz.getModifiers() & Modifier.PUBLIC ) != 0 ) { try { return clazz.getMethod( name, paramTypes ); } catch ( NoSuchMethodException e ) { /* * If the class does not have the method, then neither its * superclass nor any of its interfaces has it so quickly return * null. */ return null; } } /* * try the superclass */ Class<?> superclazz = clazz.getSuperclass(); if ( superclazz != null ) { Method superclazzMethod = getPublicMethod( superclazz, name, paramTypes ); if ( superclazzMethod != null ) { return superclazzMethod; } } /* * and interfaces */ for ( Class<?> interface_ : clazz.getInterfaces() ) { Method interfaceMethod = getPublicMethod( interface_, name, paramTypes ); if ( interfaceMethod != null ) { return interfaceMethod; } } return null; }
Used for the iterative discovery process for public methods.
/** * Used for the iterative discovery process for public methods. */
private static final class MethodInfo { Method method; String name; Class<?>[] parameterTypes; boolean upcast; MethodInfo( Method method ) { this.method = null; name = method.getName(); parameterTypes = method.getParameterTypes(); upcast = false; } void tryUpcasting( Class<?> clazz ) throws NoSuchMethodException { method = clazz.getMethod( name, parameterTypes ); name = null; parameterTypes = null; upcast = true; } } }