KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > core > BridgeMethodResolver


1 /*
2  * Copyright 2002-2007 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 JavaDoc;
20 import java.lang.reflect.Method JavaDoc;
21 import java.lang.reflect.ParameterizedType JavaDoc;
22 import java.lang.reflect.Type JavaDoc;
23 import java.lang.reflect.TypeVariable JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28
29 import org.springframework.util.Assert;
30 import org.springframework.util.ClassUtils;
31 import org.springframework.util.ReflectionUtils;
32
33 /**
34  * Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
35  * {@link Method} being bridged.
36  *
37  * <p>Given a synthetic {@link Method#isBridge bridge Method} returns the {@link Method}
38  * being bridged. A bridge method may be created by the compiler when extending a
39  * parameterized type whose methods have parameterized arguments. During runtime
40  * invocation the bridge {@link Method} may be invoked and/or used via reflection.
41  * When attempting to locate annotations on {@link Method Methods}, it is wise to check
42  * for bridge {@link Method Methods} as appropriate and find the bridged {@link Method}.
43  *
44  * <p>See <a HREF="http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5">
45  * The Java Language Specification</a> for more details on the use of bridge methods.
46  *
47  * <p>Only usable on JDK 1.5 and higher. Use an appropriate {@link JdkVersion}
48  * check before calling this class, if a fallback for JDK 1.3/1.4 is desirable.
49  *
50  * @author Rob Harrop
51  * @author Juergen Hoeller
52  * @since 2.0
53  * @see JdkVersion
54  */

55 public abstract class BridgeMethodResolver {
56
57     /**
58      * Find the original method for the supplied {@link Method bridge Method}.
59      * <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
60      * In such a case, the supplied {@link Method} instance is returned directly to the caller.
61      * Callers are <strong>not</strong> required to check for bridging before calling this method.
62      * @throws IllegalStateException if no bridged {@link Method} can be found
63      */

64     public static Method JavaDoc findBridgedMethod(Method JavaDoc bridgeMethod) {
65         Assert.notNull(bridgeMethod, "Method must not be null");
66
67         if (!bridgeMethod.isBridge()) {
68             return bridgeMethod;
69         }
70
71         // Gather all methods with matching name and parameter size.
72
List JavaDoc candidateMethods = new ArrayList JavaDoc();
73         Method JavaDoc[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
74         for (int i = 0; i < methods.length; i++) {
75             Method JavaDoc candidateMethod = methods[i];
76             if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
77                 candidateMethods.add(candidateMethod);
78             }
79         }
80
81         Method JavaDoc result;
82         // Now perform simple quick checks.
83
if (candidateMethods.size() == 1) {
84             result = (Method JavaDoc) candidateMethods.get(0);
85         }
86         else {
87             result = searchCandidates(candidateMethods, bridgeMethod);
88         }
89
90         if (result == null) {
91             throw new IllegalStateException JavaDoc(
92                     "Unable to locate bridged method for bridge method '" + bridgeMethod + "'");
93         }
94
95         return result;
96     }
97
98     /**
99      * Search for the bridged method in the given candidates.
100      * @param candidateMethods the List of candidate Methods
101      * @param bridgeMethod the bridge method
102      * @return the bridged method, or <code>null</code> if none found
103      */

104     private static Method JavaDoc searchCandidates(List JavaDoc candidateMethods, Method JavaDoc bridgeMethod) {
105         Map JavaDoc typeParameterMap = createTypeVariableMap(bridgeMethod.getDeclaringClass());
106         for (int i = 0; i < candidateMethods.size(); i++) {
107             Method JavaDoc candidateMethod = (Method JavaDoc) candidateMethods.get(i);
108             if (isBridgeMethodFor(bridgeMethod, candidateMethod, typeParameterMap)) {
109                 return candidateMethod;
110             }
111         }
112         return null;
113     }
114
115     /**
116      * Return <code>true</code> if the supplied '<code>candidateMethod</code>' can be
117      * consider a validate candidate for the {@link Method} that is {@link Method#isBridge() bridged}
118      * by the supplied {@link Method bridge Method}. This method performs inexpensive
119      * checks and can be used quickly filter for a set of possible matches.
120      */

121     private static boolean isBridgedCandidateFor(Method JavaDoc candidateMethod, Method JavaDoc bridgeMethod) {
122         return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) &&
123                 candidateMethod.getName().equals(bridgeMethod.getName()) &&
124                 candidateMethod.getParameterTypes().length == bridgeMethod.getParameterTypes().length);
125     }
126
127     /**
128      * Determine whether or not the bridge {@link Method} is the bridge for the
129      * supplied candidate {@link Method}.
130      */

131     static boolean isBridgeMethodFor(Method JavaDoc bridgeMethod, Method JavaDoc candidateMethod, Map JavaDoc typeVariableMap) {
132         if (isResolvedTypeMatch(candidateMethod, bridgeMethod, typeVariableMap)) {
133             return true;
134         }
135         Method JavaDoc method = findGenericDeclaration(bridgeMethod);
136         return (method != null ? isResolvedTypeMatch(method, candidateMethod, typeVariableMap) : false);
137     }
138
139     /**
140      * Search for the generic {@link Method} declaration whose erased signature
141      * matches that of the supplied bridge method.
142      * @throws IllegalStateException if the generic declaration cannot be found
143      */

144     private static Method JavaDoc findGenericDeclaration(Method JavaDoc bridgeMethod) {
145         // Search parent types for method that has same signature as bridge.
146
Class JavaDoc superclass = bridgeMethod.getDeclaringClass().getSuperclass();
147         while (!Object JavaDoc.class.equals(superclass)) {
148             Method JavaDoc method = searchForMatch(superclass, bridgeMethod);
149             if (method != null && !method.isBridge()) {
150                 return method;
151             }
152             superclass = superclass.getSuperclass();
153         }
154
155         // Search interfaces.
156
Class JavaDoc[] interfaces = ClassUtils.getAllInterfacesForClass(bridgeMethod.getDeclaringClass());
157         for (int i = 0; i < interfaces.length; i++) {
158             Class JavaDoc anInterface = interfaces[i];
159             Method JavaDoc method = searchForMatch(anInterface, bridgeMethod);
160             if (method != null && !method.isBridge()) {
161                 return method;
162             }
163         }
164
165         return null;
166     }
167
168     /**
169      * Return <code>true</code> if the {@link Type} signature of both the supplied
170      * {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
171      * are equal after resolving all {@link TypeVariable TypeVariables} using the supplied
172      * {@link #createTypeVariableMap TypeVariable Map}, otherwise returns <code>false</code>.
173      */

174     private static boolean isResolvedTypeMatch(Method JavaDoc genericMethod, Method JavaDoc candidateMethod, Map JavaDoc typeVariableMap) {
175         Type JavaDoc[] genericParameters = genericMethod.getGenericParameterTypes();
176         Class JavaDoc[] candidateParameters = candidateMethod.getParameterTypes();
177         if (genericParameters.length != candidateParameters.length) {
178             return false;
179         }
180         for (int i = 0; i < genericParameters.length; i++) {
181             Type JavaDoc genericParameter = genericParameters[i];
182             Class JavaDoc candidateParameter = candidateParameters[i];
183             if (genericParameter instanceof GenericArrayType JavaDoc && candidateParameter.isArray()) {
184                 // An array type: compare the component type.
185
if (!candidateParameter.getComponentType().equals(
186                         getRawType(((GenericArrayType JavaDoc) genericParameter).getGenericComponentType(), typeVariableMap))) {
187                     return false;
188                 }
189             }
190             else {
191                 // A non-array type: compare the type itself.
192
if (!candidateParameter.equals(getRawType(genericParameter, typeVariableMap))) {
193                     return false;
194                 }
195             }
196         }
197         return true;
198     }
199
200     /**
201      * Determine the raw type for the given generic parameter type.
202      */

203     private static Type JavaDoc getRawType(Type JavaDoc genericType, Map JavaDoc typeVariableMap) {
204         if (genericType instanceof TypeVariable JavaDoc) {
205             TypeVariable JavaDoc tv = (TypeVariable JavaDoc) genericType;
206             return (Type JavaDoc) typeVariableMap.get(tv.getName());
207         }
208         else if (genericType instanceof ParameterizedType JavaDoc) {
209             return ((ParameterizedType JavaDoc) genericType).getRawType();
210         }
211         else {
212             return genericType;
213         }
214     }
215
216     /**
217      * If the supplied {@link Class} has a declared {@link Method} whose signature matches
218      * that of the supplied {@link Method}, then this matching {@link Method} is returned,
219      * otherwise <code>null</code> is returned.
220      */

221     private static Method JavaDoc searchForMatch(Class JavaDoc type, Method JavaDoc bridgeMethod) {
222         return ReflectionUtils.findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes());
223     }
224
225     /**
226      * Build a mapping of {@link TypeVariable#getName TypeVariable names} to concrete
227      * {@link Class} for the specified {@link Class}. Searches all super types,
228      * enclosing types and interfaces.
229      */

230     static Map JavaDoc createTypeVariableMap(Class JavaDoc cls) {
231         Map JavaDoc typeVariableMap = new HashMap JavaDoc();
232
233         // interfaces
234
extractTypeVariablesFromGenericInterfaces(cls.getGenericInterfaces(), typeVariableMap);
235
236         // super class
237
Type JavaDoc genericType = cls.getGenericSuperclass();
238         Class JavaDoc type = cls.getSuperclass();
239         while (!Object JavaDoc.class.equals(type)) {
240             if (genericType instanceof ParameterizedType JavaDoc) {
241                 ParameterizedType JavaDoc pt = (ParameterizedType JavaDoc) genericType;
242                 populateTypeMapFromParameterizedType(typeVariableMap, pt);
243             }
244             extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(), typeVariableMap);
245             genericType = type.getGenericSuperclass();
246             type = type.getSuperclass();
247         }
248
249         // enclosing class
250
type = cls;
251         while (type.isMemberClass()) {
252             genericType = type.getGenericSuperclass();
253             if (genericType instanceof ParameterizedType JavaDoc) {
254                 ParameterizedType JavaDoc pt = (ParameterizedType JavaDoc) genericType;
255                 populateTypeMapFromParameterizedType(typeVariableMap, pt);
256             }
257             type = type.getEnclosingClass();
258         }
259
260         return typeVariableMap;
261     }
262
263     private static void extractTypeVariablesFromGenericInterfaces(Type JavaDoc[] genericInterfaces, Map JavaDoc typeVariableMap) {
264         for (int i = 0; i < genericInterfaces.length; i++) {
265             Type JavaDoc genericInterface = genericInterfaces[i];
266             if (genericInterface instanceof ParameterizedType JavaDoc) {
267                 ParameterizedType JavaDoc pt = (ParameterizedType JavaDoc) genericInterface;
268                 populateTypeMapFromParameterizedType(typeVariableMap, pt);
269             }
270             else if (genericInterface instanceof Class JavaDoc) {
271                 extractTypeVariablesFromGenericInterfaces(((Class JavaDoc)genericInterface).getGenericInterfaces(), typeVariableMap);
272             }
273         }
274     }
275
276
277     /**
278      * Read the {@link TypeVariable TypeVariables} from the supplied {@link ParameterizedType}
279      * and add mappings corresponding to the {@link TypeVariable#getName TypeVariable name} ->
280      * concrete type to the supplied {@link Map}.
281      * <p>Consider this case:
282      * <pre class="code>
283      * public interface Foo<S, T> {
284      * ..
285      * }
286      *
287      * public class FooImpl implements Foo<String, Integer> {
288      * ..
289      * }</pre>
290      * For '<code>FooImpl</code>' the following mappings would be added to the {@link Map}:
291      * {S=java.lang.String, T=java.lang.Integer}.
292      */

293     private static void populateTypeMapFromParameterizedType(Map JavaDoc typeVariableMap, ParameterizedType JavaDoc type) {
294         Type JavaDoc[] actualTypeArguments = type.getActualTypeArguments();
295         TypeVariable JavaDoc[] typeVariables = ((Class JavaDoc) type.getRawType()).getTypeParameters();
296         for (int i = 0; i < actualTypeArguments.length; i++) {
297             Type JavaDoc actualTypeArgument = actualTypeArguments[i];
298             if (actualTypeArgument instanceof Class JavaDoc) {
299                 typeVariableMap.put(typeVariables[i].getName(), (Class JavaDoc) actualTypeArgument);
300             }
301             else if (actualTypeArgument instanceof ParameterizedType JavaDoc) {
302                 typeVariableMap.put(typeVariables[i].getName(), ((ParameterizedType JavaDoc)actualTypeArgument).getRawType());
303             }
304             else if (actualTypeArgument instanceof TypeVariable JavaDoc) {
305                 // We have a type that is parameterized at instantiation time
306
// the nearest match on the bridge method will be the bounded type.
307
TypeVariable JavaDoc typeVariableArgument = (TypeVariable JavaDoc) actualTypeArgument;
308                 Class JavaDoc boundClass = extractClassTypeVariable(typeVariableArgument);
309                 if (boundClass != null) {
310                     typeVariableMap.put(typeVariables[i].getName(), boundClass);
311                 }
312             }
313         }
314     }
315
316     /**
317      * Extracts the bound '<code>Class</code>' for a give {@link TypeVariable}.
318      */

319     private static Class JavaDoc extractClassTypeVariable(TypeVariable JavaDoc typeVariable) {
320         Type JavaDoc[] bounds = typeVariable.getBounds();
321         Type JavaDoc result = null;
322         if (bounds.length > 0) {
323             Type JavaDoc bound = bounds[0];
324             if (bound instanceof ParameterizedType JavaDoc) {
325                 result = ((ParameterizedType JavaDoc) bound).getRawType();
326             }
327             else if (bound instanceof Class JavaDoc) {
328                 result = bound;
329             }
330             else if (bound instanceof TypeVariable JavaDoc) {
331                 result = extractClassTypeVariable((TypeVariable JavaDoc)bound);
332             }
333         }
334         return (result instanceof Class JavaDoc ? (Class JavaDoc) result : null);
335     }
336
337 }
338
Popular Tags