Open Source Repository

Home /spring/spring-core-3.0.5 | Repository Home



org/springframework/core/BridgeMethodResolver.java
/*
 * Copyright 2002-2009 the original author or authors.
 *
 * 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.
 */

package org.springframework.core;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

/**
 * Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
 {@link Method} being bridged.
 *
 <p>Given a synthetic {@link Method#isBridge bridge Method} returns the {@link Method}
 * being bridged. A bridge method may be created by the compiler when extending a
 * parameterized type whose methods have parameterized arguments. During runtime
 * invocation the bridge {@link Method} may be invoked and/or used via reflection.
 * When attempting to locate annotations on {@link Method Methods}, it is wise to check
 * for bridge {@link Method Methods} as appropriate and find the bridged {@link Method}.
 *
 <p>See <a href="http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5">
 * The Java Language Specification</a> for more details on the use of bridge methods.
 *
 @author Rob Harrop
 @author Juergen Hoeller
 @since 2.0
 */
public abstract class BridgeMethodResolver {

  /**
   * Find the original method for the supplied {@link Method bridge Method}.
   <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
   * In such a case, the supplied {@link Method} instance is returned directly to the caller.
   * Callers are <strong>not</strong> required to check for bridging before calling this method.
   @param bridgeMethod the method to introspect
   @return the original method (either the bridged method or the passed-in method
   * if no more specific one could be found)
   */
  public static Method findBridgedMethod(Method bridgeMethod) {
    if (bridgeMethod == null || !bridgeMethod.isBridge()) {
      return bridgeMethod;
    }
    // Gather all methods with matching name and parameter size.
    List<Method> candidateMethods = new ArrayList<Method>();
    Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
    for (Method candidateMethod : methods) {
      if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
        candidateMethods.add(candidateMethod);
      }
    }
    // Now perform simple quick check.
    if (candidateMethods.size() == 1) {
      return candidateMethods.get(0);
    }
    // Search for candidate match.
    Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod);
    if (bridgedMethod != null) {
      // Bridged method found...
      return bridgedMethod;
    }
    else {
      // A bridge method was passed in but we couldn't find the bridged method.
      // Let's proceed with the passed-in method and hope for the best...
      return bridgeMethod;
    }
  }

  /**
   * Searches for the bridged method in the given candidates.
   @param candidateMethods the List of candidate Methods
   @param bridgeMethod the bridge method
   @return the bridged method, or <code>null</code> if none found
   */
  private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
    if (candidateMethods.isEmpty()) {
      return null;
    }
    Map<TypeVariable, Type> typeParameterMap = GenericTypeResolver.getTypeVariableMap(bridgeMethod.getDeclaringClass());
    Method previousMethod = null;
    boolean sameSig = true;
    for (Method candidateMethod : candidateMethods) {
      if (isBridgeMethodFor(bridgeMethod, candidateMethod, typeParameterMap)) {
        return candidateMethod;
      }
      else if (previousMethod != null) {
        sameSig = sameSig &&
            Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
      }
      previousMethod = candidateMethod;
    }
    return (sameSig ? candidateMethods.get(0null);
  }

  /**
   * Returns <code>true</code> if the supplied '<code>candidateMethod</code>' can be
   * consider a validate candidate for the {@link Method} that is {@link Method#isBridge() bridged}
   * by the supplied {@link Method bridge Method}. This method performs inexpensive
   * checks and can be used quickly filter for a set of possible matches.
   */
  private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) {
    return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod&&
        candidateMethod.getName().equals(bridgeMethod.getName()) &&
        candidateMethod.getParameterTypes().length == bridgeMethod.getParameterTypes().length);
  }

  /**
   * Determines whether or not the bridge {@link Method} is the bridge for the
   * supplied candidate {@link Method}.
   */
  static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Map<TypeVariable, Type> typeVariableMap) {
    if (isResolvedTypeMatch(candidateMethod, bridgeMethod, typeVariableMap)) {
      return true;
    }
    Method method = findGenericDeclaration(bridgeMethod);
    return (method != null && isResolvedTypeMatch(method, candidateMethod, typeVariableMap));
  }

  /**
   * Searches for the generic {@link Method} declaration whose erased signature
   * matches that of the supplied bridge method.
   @throws IllegalStateException if the generic declaration cannot be found
   */
  private static Method findGenericDeclaration(Method bridgeMethod) {
    // Search parent types for method that has same signature as bridge.
    Class superclass = bridgeMethod.getDeclaringClass().getSuperclass();
    while (!Object.class.equals(superclass)) {
      Method method = searchForMatch(superclass, bridgeMethod);
      if (method != null && !method.isBridge()) {
        return method;
      }
      superclass = superclass.getSuperclass();
    }

    // Search interfaces.
    Class[] interfaces = ClassUtils.getAllInterfacesForClass(bridgeMethod.getDeclaringClass());
    for (Class ifc : interfaces) {
      Method method = searchForMatch(ifc, bridgeMethod);
      if (method != null && !method.isBridge()) {
        return method;
      }
    }

    return null;
  }

  /**
   * Returns <code>true</code> if the {@link Type} signature of both the supplied
   {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
   * are equal after resolving all {@link TypeVariable TypeVariables} using the supplied
   * TypeVariable Map, otherwise returns <code>false</code>.
   */
  private static boolean isResolvedTypeMatch(
      Method genericMethod, Method candidateMethod, Map<TypeVariable, Type> typeVariableMap) {

    Type[] genericParameters = genericMethod.getGenericParameterTypes();
    Class[] candidateParameters = candidateMethod.getParameterTypes();
    if (genericParameters.length != candidateParameters.length) {
      return false;
    }
    for (int i = 0; i < genericParameters.length; i++) {
      Type genericParameter = genericParameters[i];
      Class candidateParameter = candidateParameters[i];
      if (candidateParameter.isArray()) {
        // An array type: compare the component type.
        Type rawType = GenericTypeResolver.getRawType(genericParameter, typeVariableMap);
        if (rawType instanceof GenericArrayType) {
          if (!candidateParameter.getComponentType().equals(
              GenericTypeResolver.resolveType(((GenericArrayTyperawType).getGenericComponentType(), typeVariableMap))) {
            return false;
          }
          break;
        }
      }
      // A non-array type: compare the type itself.
      Class resolvedParameter = GenericTypeResolver.resolveType(genericParameter, typeVariableMap);
      if (!candidateParameter.equals(resolvedParameter)) {
        return false;
      }
    }
    return true;
  }

  /**
   * If the supplied {@link Class} has a declared {@link Method} whose signature matches
   * that of the supplied {@link Method}, then this matching {@link Method} is returned,
   * otherwise <code>null</code> is returned.
   */
  private static Method searchForMatch(Class type, Method bridgeMethod) {
    return ReflectionUtils.findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes());
  }

}