001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core;
018
019import java.lang.reflect.Method;
020import java.lang.reflect.Type;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.List;
024
025import org.springframework.util.ClassUtils;
026import org.springframework.util.ReflectionUtils;
027
028/**
029 * Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
030 * {@link Method} being bridged.
031 *
032 * <p>Given a synthetic {@link Method#isBridge bridge Method} returns the {@link Method}
033 * being bridged. A bridge method may be created by the compiler when extending a
034 * parameterized type whose methods have parameterized arguments. During runtime
035 * invocation the bridge {@link Method} may be invoked and/or used via reflection.
036 * When attempting to locate annotations on {@link Method Methods}, it is wise to check
037 * for bridge {@link Method Methods} as appropriate and find the bridged {@link Method}.
038 *
039 * <p>See <a href="https://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5">
040 * The Java Language Specification</a> for more details on the use of bridge methods.
041 *
042 * @author Rob Harrop
043 * @author Juergen Hoeller
044 * @author Phillip Webb
045 * @since 2.0
046 */
047public abstract class BridgeMethodResolver {
048
049        /**
050         * Find the original method for the supplied {@link Method bridge Method}.
051         * <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
052         * In such a case, the supplied {@link Method} instance is returned directly to the caller.
053         * Callers are <strong>not</strong> required to check for bridging before calling this method.
054         * @param bridgeMethod the method to introspect
055         * @return the original method (either the bridged method or the passed-in method
056         * if no more specific one could be found)
057         */
058        public static Method findBridgedMethod(Method bridgeMethod) {
059                if (bridgeMethod == null || !bridgeMethod.isBridge()) {
060                        return bridgeMethod;
061                }
062
063                // Gather all methods with matching name and parameter size.
064                List<Method> candidateMethods = new ArrayList<Method>();
065                Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
066                for (Method candidateMethod : methods) {
067                        if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
068                                candidateMethods.add(candidateMethod);
069                        }
070                }
071
072                // Now perform simple quick check.
073                if (candidateMethods.size() == 1) {
074                        return candidateMethods.get(0);
075                }
076
077                // Search for candidate match.
078                Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod);
079                if (bridgedMethod != null) {
080                        // Bridged method found...
081                        return bridgedMethod;
082                }
083                else {
084                        // A bridge method was passed in but we couldn't find the bridged method.
085                        // Let's proceed with the passed-in method and hope for the best...
086                        return bridgeMethod;
087                }
088        }
089
090        /**
091         * Returns {@code true} if the supplied '{@code candidateMethod}' can be
092         * consider a validate candidate for the {@link Method} that is {@link Method#isBridge() bridged}
093         * by the supplied {@link Method bridge Method}. This method performs inexpensive
094         * checks and can be used quickly filter for a set of possible matches.
095         */
096        private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) {
097                return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) &&
098                                candidateMethod.getName().equals(bridgeMethod.getName()) &&
099                                candidateMethod.getParameterTypes().length == bridgeMethod.getParameterTypes().length);
100        }
101
102        /**
103         * Searches for the bridged method in the given candidates.
104         * @param candidateMethods the List of candidate Methods
105         * @param bridgeMethod the bridge method
106         * @return the bridged method, or {@code null} if none found
107         */
108        private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
109                if (candidateMethods.isEmpty()) {
110                        return null;
111                }
112                Method previousMethod = null;
113                boolean sameSig = true;
114                for (Method candidateMethod : candidateMethods) {
115                        if (isBridgeMethodFor(bridgeMethod, candidateMethod, bridgeMethod.getDeclaringClass())) {
116                                return candidateMethod;
117                        }
118                        else if (previousMethod != null) {
119                                sameSig = sameSig &&
120                                                Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
121                        }
122                        previousMethod = candidateMethod;
123                }
124                return (sameSig ? candidateMethods.get(0) : null);
125        }
126
127        /**
128         * Determines whether or not the bridge {@link Method} is the bridge for the
129         * supplied candidate {@link Method}.
130         */
131        static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Class<?> declaringClass) {
132                if (isResolvedTypeMatch(candidateMethod, bridgeMethod, declaringClass)) {
133                        return true;
134                }
135                Method method = findGenericDeclaration(bridgeMethod);
136                return (method != null && isResolvedTypeMatch(method, candidateMethod, declaringClass));
137        }
138
139        /**
140         * Returns {@code true} if the {@link Type} signature of both the supplied
141         * {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
142         * are equal after resolving all types against the declaringType, otherwise
143         * returns {@code false}.
144         */
145        private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> declaringClass) {
146                Type[] genericParameters = genericMethod.getGenericParameterTypes();
147                Class<?>[] candidateParameters = candidateMethod.getParameterTypes();
148                if (genericParameters.length != candidateParameters.length) {
149                        return false;
150                }
151                for (int i = 0; i < candidateParameters.length; i++) {
152                        ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, declaringClass);
153                        Class<?> candidateParameter = candidateParameters[i];
154                        if (candidateParameter.isArray()) {
155                                // An array type: compare the component type.
156                                if (!candidateParameter.getComponentType().equals(genericParameter.getComponentType().resolve(Object.class))) {
157                                        return false;
158                                }
159                        }
160                        // A non-array type: compare the type itself.
161                        if (!candidateParameter.equals(genericParameter.resolve(Object.class))) {
162                                return false;
163                        }
164                }
165                return true;
166        }
167
168        /**
169         * Searches for the generic {@link Method} declaration whose erased signature
170         * matches that of the supplied bridge method.
171         * @throws IllegalStateException if the generic declaration cannot be found
172         */
173        private static Method findGenericDeclaration(Method bridgeMethod) {
174                // Search parent types for method that has same signature as bridge.
175                Class<?> superclass = bridgeMethod.getDeclaringClass().getSuperclass();
176                while (superclass != null && Object.class != superclass) {
177                        Method method = searchForMatch(superclass, bridgeMethod);
178                        if (method != null && !method.isBridge()) {
179                                return method;
180                        }
181                        superclass = superclass.getSuperclass();
182                }
183
184                Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(bridgeMethod.getDeclaringClass());
185                return searchInterfaces(interfaces, bridgeMethod);
186        }
187
188        private static Method searchInterfaces(Class<?>[] interfaces, Method bridgeMethod) {
189                for (Class<?> ifc : interfaces) {
190                        Method method = searchForMatch(ifc, bridgeMethod);
191                        if (method != null && !method.isBridge()) {
192                                return method;
193                        }
194                        else {
195                                method = searchInterfaces(ifc.getInterfaces(), bridgeMethod);
196                                if (method != null) {
197                                        return method;
198                                }
199                        }
200                }
201                return null;
202        }
203
204        /**
205         * If the supplied {@link Class} has a declared {@link Method} whose signature matches
206         * that of the supplied {@link Method}, then this matching {@link Method} is returned,
207         * otherwise {@code null} is returned.
208         */
209        private static Method searchForMatch(Class<?> type, Method bridgeMethod) {
210                try {
211                        return type.getDeclaredMethod(bridgeMethod.getName(), bridgeMethod.getParameterTypes());
212                }
213                catch (NoSuchMethodException ex) {
214                        return null;
215                }
216        }
217
218        /**
219         * Compare the signatures of the bridge method and the method which it bridges. If
220         * the parameter and return types are the same, it is a 'visibility' bridge method
221         * introduced in Java 6 to fix https://bugs.java.com/view_bug.do?bug_id=6342411.
222         * See also https://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
223         * @return whether signatures match as described
224         */
225        public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
226                if (bridgeMethod == bridgedMethod) {
227                        return true;
228                }
229                return (bridgeMethod.getReturnType().equals(bridgedMethod.getReturnType()) &&
230                                Arrays.equals(bridgeMethod.getParameterTypes(), bridgedMethod.getParameterTypes()));
231        }
232
233}