Save This Page
Home » Spring-Framework-090522 » org.springframework » core » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.core;
   18   
   19   import java.lang.reflect.GenericArrayType;
   20   import java.lang.reflect.Method;
   21   import java.lang.reflect.Type;
   22   import java.lang.reflect.TypeVariable;
   23   import java.util.ArrayList;
   24   import java.util.List;
   25   import java.util.Map;
   26   
   27   import org.springframework.util.Assert;
   28   import org.springframework.util.ClassUtils;
   29   import org.springframework.util.ReflectionUtils;
   30   
   31   /**
   32    * Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
   33    * {@link Method} being bridged.
   34    *
   35    * <p>Given a synthetic {@link Method#isBridge bridge Method} returns the {@link Method}
   36    * being bridged. A bridge method may be created by the compiler when extending a
   37    * parameterized type whose methods have parameterized arguments. During runtime
   38    * invocation the bridge {@link Method} may be invoked and/or used via reflection.
   39    * When attempting to locate annotations on {@link Method Methods}, it is wise to check
   40    * for bridge {@link Method Methods} as appropriate and find the bridged {@link Method}.
   41    *
   42    * <p>See <a href="http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5">
   43    * The Java Language Specification</a> for more details on the use of bridge methods.
   44    *
   45    * <p>Only usable on JDK 1.5 and higher. Use an appropriate {@link JdkVersion}
   46    * check before calling this class if a fallback for JDK 1.4 is desirable.
   47    *
   48    * @author Rob Harrop
   49    * @author Juergen Hoeller
   50    * @since 2.0
   51    * @see JdkVersion
   52    */
   53   public abstract class BridgeMethodResolver {
   54   
   55   	/**
   56   	 * Find the original method for the supplied {@link Method bridge Method}.
   57   	 * <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
   58   	 * In such a case, the supplied {@link Method} instance is returned directly to the caller.
   59   	 * Callers are <strong>not</strong> required to check for bridging before calling this method.
   60   	 * @throws IllegalStateException if no bridged {@link Method} can be found
   61   	 */
   62   	public static Method findBridgedMethod(Method bridgeMethod) {
   63   		Assert.notNull(bridgeMethod, "Method must not be null");
   64   
   65   		if (!bridgeMethod.isBridge()) {
   66   			return bridgeMethod;
   67   		}
   68   
   69   		// Gather all methods with matching name and parameter size.
   70   		List candidateMethods = new ArrayList();
   71   		Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
   72   		for (int i = 0; i < methods.length; i++) {
   73   			Method candidateMethod = methods[i];
   74   			if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
   75   				candidateMethods.add(candidateMethod);
   76   			}
   77   		}
   78   
   79   		Method result;
   80   		// Now perform simple quick checks.
   81   		if (candidateMethods.size() == 1) {
   82   			result = (Method) candidateMethods.get(0);
   83   		}
   84   		else {
   85   			result = searchCandidates(candidateMethods, bridgeMethod);
   86   		}
   87   
   88   		if (result == null) {
   89   			throw new IllegalStateException(
   90   					"Unable to locate bridged method for bridge method '" + bridgeMethod + "'");
   91   		}
   92   
   93   		return result;
   94   	}
   95   
   96   	/**
   97   	 * Searches for the bridged method in the given candidates.
   98   	 * @param candidateMethods the List of candidate Methods
   99   	 * @param bridgeMethod the bridge method
  100   	 * @return the bridged method, or <code>null</code> if none found
  101   	 */
  102   	private static Method searchCandidates(List candidateMethods, Method bridgeMethod) {
  103   		Map typeParameterMap = GenericTypeResolver.getTypeVariableMap(bridgeMethod.getDeclaringClass());
  104   		for (int i = 0; i < candidateMethods.size(); i++) {
  105   			Method candidateMethod = (Method) candidateMethods.get(i);
  106   			if (isBridgeMethodFor(bridgeMethod, candidateMethod, typeParameterMap)) {
  107   				return candidateMethod;
  108   			}
  109   		}
  110   		return null;
  111   	}
  112   
  113   	/**
  114   	 * Returns <code>true</code> if the supplied '<code>candidateMethod</code>' can be
  115   	 * consider a validate candidate for the {@link Method} that is {@link Method#isBridge() bridged}
  116   	 * by the supplied {@link Method bridge Method}. This method performs inexpensive
  117   	 * checks and can be used quickly filter for a set of possible matches.
  118   	 */
  119   	private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) {
  120   		return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) &&
  121   				candidateMethod.getName().equals(bridgeMethod.getName()) &&
  122   				candidateMethod.getParameterTypes().length == bridgeMethod.getParameterTypes().length);
  123   	}
  124   
  125   	/**
  126   	 * Determines whether or not the bridge {@link Method} is the bridge for the
  127   	 * supplied candidate {@link Method}.
  128   	 */
  129   	static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Map typeVariableMap) {
  130   		if (isResolvedTypeMatch(candidateMethod, bridgeMethod, typeVariableMap)) {
  131   			return true;
  132   		}
  133   		Method method = findGenericDeclaration(bridgeMethod);
  134   		return (method != null && isResolvedTypeMatch(method, candidateMethod, typeVariableMap));
  135   	}
  136   
  137   	/**
  138   	 * Searches for the generic {@link Method} declaration whose erased signature
  139   	 * matches that of the supplied bridge method.
  140   	 * @throws IllegalStateException if the generic declaration cannot be found
  141   	 */
  142   	private static Method findGenericDeclaration(Method bridgeMethod) {
  143   		// Search parent types for method that has same signature as bridge.
  144   		Class superclass = bridgeMethod.getDeclaringClass().getSuperclass();
  145   		while (!Object.class.equals(superclass)) {
  146   			Method method = searchForMatch(superclass, bridgeMethod);
  147   			if (method != null && !method.isBridge()) {
  148   				return method;
  149   			}
  150   			superclass = superclass.getSuperclass();
  151   		}
  152   
  153   		// Search interfaces.
  154   		Class[] interfaces = ClassUtils.getAllInterfacesForClass(bridgeMethod.getDeclaringClass());
  155   		for (int i = 0; i < interfaces.length; i++) {
  156   			Class anInterface = interfaces[i];
  157   			Method method = searchForMatch(anInterface, bridgeMethod);
  158   			if (method != null && !method.isBridge()) {
  159   				return method;
  160   			}
  161   		}
  162   
  163   		return null;
  164   	}
  165   
  166   	/**
  167   	 * Returns <code>true</code> if the {@link Type} signature of both the supplied
  168   	 * {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
  169   	 * are equal after resolving all {@link TypeVariable TypeVariables} using the supplied
  170   	 * TypeVariable Map, otherwise returns <code>false</code>.
  171   	 */
  172   	private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Map typeVariableMap) {
  173   		Type[] genericParameters = genericMethod.getGenericParameterTypes();
  174   		Class[] candidateParameters = candidateMethod.getParameterTypes();
  175   		if (genericParameters.length != candidateParameters.length) {
  176   			return false;
  177   		}
  178   		for (int i = 0; i < genericParameters.length; i++) {
  179   			Type genericParameter = genericParameters[i];
  180   			Class candidateParameter = candidateParameters[i];
  181   			if (candidateParameter.isArray()) {
  182   				// An array type: compare the component type.
  183   				Type rawType = GenericTypeResolver.getRawType(genericParameter, typeVariableMap);
  184   				if (rawType instanceof GenericArrayType) {
  185   					if (!candidateParameter.getComponentType().equals(
  186   							GenericTypeResolver.resolveType(((GenericArrayType) rawType).getGenericComponentType(), typeVariableMap))) {
  187   						return false;
  188   					}
  189   					break;
  190   				}
  191   			}
  192   			// A non-array type: compare the type itself.
  193   			if (!candidateParameter.equals(GenericTypeResolver.resolveType(genericParameter, typeVariableMap))) {
  194   				return false;
  195   			}
  196   		}
  197   		return true;
  198   	}
  199   
  200   	/**
  201   	 * If the supplied {@link Class} has a declared {@link Method} whose signature matches
  202   	 * that of the supplied {@link Method}, then this matching {@link Method} is returned,
  203   	 * otherwise <code>null</code> is returned.
  204   	 */
  205   	private static Method searchForMatch(Class type, Method bridgeMethod) {
  206   		return ReflectionUtils.findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes());
  207   	}
  208   
  209   }

Save This Page
Home » Spring-Framework-090522 » org.springframework » core » [javadoc | source]