Clover Coverage Report - EasyMock 3.0
Coverage timestamp: sam. mai 8 2010 14:37:27 CEST
0   445   0   -
0   241   -   0
0     -  
1    
100% of code in this file is excluded from these metrics.
 
  BridgeMethodResolver       Line # 46 0 100% 0 0 - -1.0
 
No Tests
 
1    /**
2    * Copyright 2001-2010 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.easymock.internal;
18   
19    import java.lang.reflect.*;
20    import java.util.*;
21   
22    /**
23    * Code taken from the <a href="http://www.springframework.org">Spring
24    * framework</a>.
25    *
26    * Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
27    * {@link Method} being bridged.
28    *
29    * <p>
30    * Given a synthetic {@link Method#isBridge bridge Method} returns the
31    * {@link Method} being bridged. A bridge method may be created by the compiler
32    * when extending a parameterized type whose methods have parameterized
33    * arguments. During runtime invocation the bridge {@link Method} may be invoked
34    * and/or used via reflection. When attempting to locate annotations on
35    * {@link Method Methods}, it is wise to check for bridge {@link Method Methods}
36    * as appropriate and find the bridged {@link Method}.
37    *
38    * <p>
39    * See <a href="http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5"
40    * > The Java Language Specification</a> for more details on the use of bridge
41    * methods.
42    *
43    * @author Rob Harrop
44    * @author Juergen Hoeller
45    */
 
46    public final class BridgeMethodResolver {
47   
48    // Hard to test all cases since bridges varies from JVM implementation
49    // Plus, the code is taken from Spring so we consider it is working
50    // So, don't check coverage over the class
51   
52    // ///CLOVER:OFF
 
53    toggle private BridgeMethodResolver() {
54    }
55   
56    /**
57    * Find the original method for the supplied {@link Method bridge Method}.
58    * <p>
59    * It is safe to call this method passing in a non-bridge {@link Method}
60    * instance. In such a case, the supplied {@link Method} instance is
61    * returned directly to the caller. Callers are <strong>not</strong>
62    * required to check for bridging before calling this method.
63    *
64    * @param bridgeMethod
65    * the bridge method
66    * @return the original method for the bridge
67    * @throws IllegalStateException
68    * if no bridged {@link Method} can be found
69    */
 
70    toggle public static Method findBridgedMethod(final Method bridgeMethod) {
71    assert bridgeMethod != null : "Method must not be null";
72   
73    if (!bridgeMethod.isBridge()) {
74    return bridgeMethod;
75    }
76   
77    // Gather all methods with matching name and parameter size.
78    final List<Method> candidateMethods = new ArrayList<Method>();
79    final Method[] methods = getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
80    for (final Method candidateMethod : methods) {
81    if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
82    candidateMethods.add(candidateMethod);
83    }
84    }
85   
86    Method result;
87    // Now perform simple quick checks.
88    if (candidateMethods.size() == 1) {
89    result = candidateMethods.get(0);
90    } else {
91    result = searchCandidates(candidateMethods, bridgeMethod);
92    }
93   
94    if (result == null) {
95    throw new IllegalStateException("Unable to locate bridged method for bridge method '"
96    + bridgeMethod + "'");
97    }
98   
99    return result;
100    }
101   
102    /**
103    * Search for the bridged method in the given candidates.
104    *
105    * @param candidateMethods
106    * the List of candidate Methods
107    * @param bridgeMethod
108    * the bridge method
109    * @return the bridged method, or <code>null</code> if none found
110    */
 
111    toggle private static Method searchCandidates(final List<Method> candidateMethods, final Method bridgeMethod) {
112    final Map<TypeVariable<?>, Type> typeParameterMap = createTypeVariableMap(bridgeMethod
113    .getDeclaringClass());
114    for (int i = 0; i < candidateMethods.size(); i++) {
115    final Method candidateMethod = candidateMethods.get(i);
116    if (isBridgeMethodFor(bridgeMethod, candidateMethod, typeParameterMap)) {
117    return candidateMethod;
118    }
119    }
120    return null;
121    }
122   
123    /**
124    * Return <code>true</code> if the supplied '<code>candidateMethod</code>'
125    * can be consider a validate candidate for the {@link Method} that is
126    * {@link Method#isBridge() bridged} by the supplied {@link Method bridge
127    * Method}. This method performs inexpensive checks and can be used quickly
128    * filter for a set of possible matches.
129    */
 
130    toggle private static boolean isBridgedCandidateFor(final Method candidateMethod, final Method bridgeMethod) {
131    return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod)
132    && candidateMethod.getName().equals(bridgeMethod.getName()) && candidateMethod
133    .getParameterTypes().length == bridgeMethod.getParameterTypes().length);
134    }
135   
136    /**
137    * Determine whether or not the bridge {@link Method} is the bridge for the
138    * supplied candidate {@link Method}.
139    */
 
140    toggle private static boolean isBridgeMethodFor(final Method bridgeMethod, final Method candidateMethod,
141    final Map<TypeVariable<?>, Type> typeVariableMap) {
142    if (isResolvedTypeMatch(candidateMethod, bridgeMethod, typeVariableMap)) {
143    return true;
144    }
145    final Method method = findGenericDeclaration(bridgeMethod);
146    return (method != null ? isResolvedTypeMatch(method, candidateMethod, typeVariableMap) : false);
147    }
148   
149    /**
150    * Search for the generic {@link Method} declaration whose erased signature
151    * matches that of the supplied bridge method.
152    *
153    * @throws IllegalStateException
154    * if the generic declaration cannot be found
155    */
 
156    toggle private static Method findGenericDeclaration(final Method bridgeMethod) {
157    // Search parent types for method that has same signature as bridge.
158    Class<?> superclass = bridgeMethod.getDeclaringClass().getSuperclass();
159    while (!Object.class.equals(superclass)) {
160    final Method method = searchForMatch(superclass, bridgeMethod);
161    if (method != null && !method.isBridge()) {
162    return method;
163    }
164    superclass = superclass.getSuperclass();
165    }
166   
167    // Search interfaces.
168    final Class<?>[] interfaces = getAllInterfacesForClass(bridgeMethod.getDeclaringClass());
169    for (final Class<?> anInterface : interfaces) {
170    final Method method = searchForMatch(anInterface, bridgeMethod);
171    if (method != null && !method.isBridge()) {
172    return method;
173    }
174    }
175   
176    return null;
177    }
178   
179    /**
180    * Return <code>true</code> if the {@link Type} signature of both the
181    * supplied {@link Method#getGenericParameterTypes() generic Method} and
182    * concrete {@link Method} are equal after resolving all
183    * {@link TypeVariable TypeVariables} using the supplied
184    * {@link #createTypeVariableMap TypeVariable Map}, otherwise returns
185    * <code>false</code>.
186    */
 
187    toggle private static boolean isResolvedTypeMatch(final Method genericMethod, final Method candidateMethod,
188    final Map<TypeVariable<?>, Type> typeVariableMap) {
189    final Type[] genericParameters = genericMethod.getGenericParameterTypes();
190    final Class<?>[] candidateParameters = candidateMethod.getParameterTypes();
191    if (genericParameters.length != candidateParameters.length) {
192    return false;
193    }
194    for (int i = 0; i < genericParameters.length; i++) {
195    final Type genericParameter = genericParameters[i];
196    final Class<?> candidateParameter = candidateParameters[i];
197    if (candidateParameter.isArray()) {
198    // An array type: compare the component type.
199    final Type rawType = getRawType(genericParameter, typeVariableMap);
200    if (rawType instanceof GenericArrayType) {
201    if (!candidateParameter.getComponentType().equals(
202    getRawType(((GenericArrayType) rawType).getGenericComponentType(),
203    typeVariableMap))) {
204    return false;
205    }
206    break;
207    }
208    }
209    // A non-array type: compare the type itself.
210    if (!candidateParameter.equals(getRawType(genericParameter, typeVariableMap))) {
211    return false;
212    }
213    }
214    return true;
215    }
216   
217    /**
218    * Determine the raw type for the given generic parameter type.
219    */
 
220    toggle private static Type getRawType(final Type genericType, final Map<TypeVariable<?>, Type> typeVariableMap) {
221    if (genericType instanceof TypeVariable<?>) {
222    final TypeVariable<?> tv = (TypeVariable<?>) genericType;
223    final Type result = typeVariableMap.get(tv);
224    return (result != null ? result : Object.class);
225    } else if (genericType instanceof ParameterizedType) {
226    return ((ParameterizedType) genericType).getRawType();
227    } else {
228    return genericType;
229    }
230    }
231   
232    /**
233    * If the supplied {@link Class} has a declared {@link Method} whose
234    * signature matches that of the supplied {@link Method}, then this matching
235    * {@link Method} is returned, otherwise <code>null</code> is returned.
236    */
 
237    toggle private static Method searchForMatch(final Class<?> type, final Method bridgeMethod) {
238    return findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes());
239    }
240   
241    /**
242    * Build a mapping of {@link TypeVariable#getName TypeVariable names} to
243    * concrete {@link Class} for the specified {@link Class}. Searches all
244    * super types, enclosing types and interfaces.
245    */
 
246    toggle private static Map<TypeVariable<?>, Type> createTypeVariableMap(final Class<?> cls) {
247    final Map<TypeVariable<?>, Type> typeVariableMap = new HashMap<TypeVariable<?>, Type>();
248   
249    // interfaces
250    extractTypeVariablesFromGenericInterfaces(cls.getGenericInterfaces(), typeVariableMap);
251   
252    // super class
253    Type genericType = cls.getGenericSuperclass();
254    Class<?> type = cls.getSuperclass();
255    while (!Object.class.equals(type)) {
256    if (genericType instanceof ParameterizedType) {
257    final ParameterizedType pt = (ParameterizedType) genericType;
258    populateTypeMapFromParameterizedType(pt, typeVariableMap);
259    }
260    extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(), typeVariableMap);
261    genericType = type.getGenericSuperclass();
262    type = type.getSuperclass();
263    }
264   
265    // enclosing class
266    type = cls;
267    while (type.isMemberClass()) {
268    genericType = type.getGenericSuperclass();
269    if (genericType instanceof ParameterizedType) {
270    final ParameterizedType pt = (ParameterizedType) genericType;
271    populateTypeMapFromParameterizedType(pt, typeVariableMap);
272    }
273    type = type.getEnclosingClass();
274    }
275   
276    return typeVariableMap;
277    }
278   
 
279    toggle private static void extractTypeVariablesFromGenericInterfaces(final Type[] genericInterfaces,
280    final Map<TypeVariable<?>, Type> typeVariableMap) {
281    for (final Type genericInterface : genericInterfaces) {
282    if (genericInterface instanceof ParameterizedType) {
283    final ParameterizedType pt = (ParameterizedType) genericInterface;
284    populateTypeMapFromParameterizedType(pt, typeVariableMap);
285    if (pt.getRawType() instanceof Class<?>) {
286    extractTypeVariablesFromGenericInterfaces(((Class<?>) pt.getRawType())
287    .getGenericInterfaces(), typeVariableMap);
288    }
289    } else if (genericInterface instanceof Class<?>) {
290    extractTypeVariablesFromGenericInterfaces(((Class<?>) genericInterface)
291    .getGenericInterfaces(), typeVariableMap);
292    }
293    }
294    }
295   
296    /**
297    * Read the {@link TypeVariable TypeVariables} from the supplied
298    * {@link ParameterizedType} and add mappings corresponding to the
299    * {@link TypeVariable#getName TypeVariable name} -> concrete type to the
300    * supplied {@link Map}.
301    * <p>
302    * Consider this case:
303    *
304    * <pre class="code>
305    * public interface Foo&lt;S, T&gt; {
306    * ..
307    * }
308    *
309    * public class FooImpl implements Foo&lt;String, Integer&gt; {
310    * ..
311    * }
312    * </pre>
313    *
314    * For '<code>FooImpl</code>' the following mappings would be added to the
315    * {@link Map}: {S=java.lang.String, T=java.lang.Integer}.
316    */
 
317    toggle private static void populateTypeMapFromParameterizedType(final ParameterizedType type,
318    final Map<TypeVariable<?>, Type> typeVariableMap) {
319    if (type.getRawType() instanceof Class<?>) {
320    final Type[] actualTypeArguments = type.getActualTypeArguments();
321    final TypeVariable<?>[] typeVariables = ((Class<?>) type.getRawType()).getTypeParameters();
322    for (int i = 0; i < actualTypeArguments.length; i++) {
323    final Type actualTypeArgument = actualTypeArguments[i];
324    final TypeVariable<?> variable = typeVariables[i];
325    if (actualTypeArgument instanceof Class<?>) {
326    typeVariableMap.put(variable, actualTypeArgument);
327    } else if (actualTypeArgument instanceof GenericArrayType) {
328    typeVariableMap.put(variable, actualTypeArgument);
329    } else if (actualTypeArgument instanceof ParameterizedType) {
330    typeVariableMap.put(variable, ((ParameterizedType) actualTypeArgument).getRawType());
331    } else if (actualTypeArgument instanceof TypeVariable<?>) {
332    // We have a type that is parameterized at instantiation time
333    // the nearest match on the bridge method will be the bounded type.
334    final TypeVariable<?> typeVariableArgument = (TypeVariable<?>) actualTypeArgument;
335    Type resolvedType = typeVariableMap.get(typeVariableArgument);
336    if (resolvedType == null) {
337    resolvedType = extractClassForTypeVariable(typeVariableArgument);
338    }
339    if (resolvedType != null) {
340    typeVariableMap.put(variable, resolvedType);
341    }
342    }
343    }
344    }
345    }
346   
347    /**
348    * Extracts the bound '<code>Class</code>' for a give {@link TypeVariable}.
349    */
 
350    toggle private static Class<?> extractClassForTypeVariable(final TypeVariable<?> typeVariable) {
351    final Type[] bounds = typeVariable.getBounds();
352    Type result = null;
353    if (bounds.length > 0) {
354    final Type bound = bounds[0];
355    if (bound instanceof ParameterizedType) {
356    result = ((ParameterizedType) bound).getRawType();
357    } else if (bound instanceof Class<?>) {
358    result = bound;
359    } else if (bound instanceof TypeVariable<?>) {
360    result = extractClassForTypeVariable((TypeVariable<?>) bound);
361    }
362    }
363    return (result instanceof Class<?> ? (Class<?>) result : null);
364    }
365   
366    /**
367    * Return all interfaces that the given class implements as array, including
368    * ones implemented by superclasses.
369    * <p>
370    * If the class itself is an interface, it gets returned as sole interface.
371    *
372    * @param clazz
373    * the class to analyse for interfaces
374    * @return all interfaces that the given object implements as array
375    */
 
376    toggle private static Class<?>[] getAllInterfacesForClass(Class<?> clazz) {
377    assert clazz != null : "Class must not be null";
378    if (clazz.isInterface()) {
379    return new Class[] { clazz };
380    }
381    final List<Class<?>> interfaces = new ArrayList<Class<?>>();
382    while (clazz != null) {
383    for (int i = 0; i < clazz.getInterfaces().length; i++) {
384    final Class<?> ifc = clazz.getInterfaces()[i];
385    if (!interfaces.contains(ifc)) {
386    interfaces.add(ifc);
387    }
388    }
389    clazz = clazz.getSuperclass();
390    }
391    return interfaces.toArray(new Class[interfaces.size()]);
392    }
393   
394    /**
395    * Attempt to find a {@link Method} on the supplied class with the supplied
396    * name and parameter types. Searches all superclasses up to
397    * <code>Object</code>.
398    * <p>
399    * Returns <code>null</code> if no {@link Method} can be found.
400    *
401    * @param clazz
402    * the class to introspect
403    * @param name
404    * the name of the method
405    * @param paramTypes
406    * the parameter types of the method
407    * @return the Method object, or <code>null</code> if none found
408    */
 
409    toggle private static Method findMethod(final Class<?> clazz, final String name, final Class<?>[] paramTypes) {
410    assert clazz != null : "Class must not be null";
411    assert name != null : "Method name must not be null";
412    Class<?> searchType = clazz;
413    while (!Object.class.equals(searchType) && searchType != null) {
414    final Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType
415    .getDeclaredMethods());
416    for (final Method method : methods) {
417    if (name.equals(method.getName()) && Arrays.equals(paramTypes, method.getParameterTypes())) {
418    return method;
419    }
420    }
421    searchType = searchType.getSuperclass();
422    }
423    return null;
424    }
425   
426    /**
427    * Get all declared methods on the leaf class and all superclasses. Leaf
428    * class methods are included first.
429    */
 
430    toggle private static Method[] getAllDeclaredMethods(Class<?> leafClass) {
431    final List<Method> list = new LinkedList<Method>();
432   
433    // Keep backing up the inheritance hierarchy.
434    do {
435    final Method[] methods = leafClass.getDeclaredMethods();
436    for (final Method method : methods) {
437    list.add(method);
438    }
439    leafClass = leafClass.getSuperclass();
440    } while (leafClass != null);
441   
442    return list.toArray(new Method[list.size()]);
443    }
444    // ///CLOVER:ON
445    }