|
21 | 21 |
|
22 | 22 | import com.fasterxml.classmate.MemberResolver; |
23 | 23 | import com.fasterxml.classmate.ResolvedType; |
24 | | -import com.fasterxml.classmate.ResolvedTypeWithMembers; |
25 | 24 | import com.fasterxml.classmate.TypeResolver; |
26 | 25 | import com.fasterxml.classmate.members.ResolvedMethod; |
27 | 26 | import com.google.common.annotations.VisibleForTesting; |
| 27 | +import com.google.common.base.Function; |
| 28 | +import com.google.common.base.Optional; |
28 | 29 | import com.google.common.base.Predicate; |
29 | | -import com.google.common.collect.Iterables; |
| 30 | +import com.google.common.collect.Lists; |
30 | 31 | import com.google.common.collect.Ordering; |
31 | 32 | import com.google.common.primitives.Ints; |
32 | | -import org.slf4j.Logger; |
33 | | -import org.slf4j.LoggerFactory; |
34 | | -import org.springframework.core.LocalVariableTableParameterNameDiscoverer; |
35 | 33 | import org.springframework.core.MethodParameter; |
36 | 34 | import org.springframework.web.method.HandlerMethod; |
37 | 35 | import springfox.documentation.service.ResolvedMethodParameter; |
38 | | -import springfox.documentation.spring.web.HandlerMethodReturnTypes; |
39 | 36 |
|
40 | 37 | import java.lang.reflect.Method; |
41 | 38 | import java.lang.reflect.ParameterizedType; |
| 39 | +import java.lang.reflect.Proxy; |
42 | 40 | import java.lang.reflect.Type; |
43 | 41 | import java.util.Comparator; |
44 | 42 | import java.util.List; |
45 | 43 |
|
46 | | -import static com.google.common.collect.Iterables.*; |
| 44 | +import static com.google.common.base.Optional.*; |
| 45 | +import static com.google.common.collect.FluentIterable.*; |
47 | 46 | import static com.google.common.collect.Lists.*; |
48 | 47 |
|
49 | 48 | public class HandlerMethodResolver { |
50 | 49 |
|
51 | | - private static final Logger log = LoggerFactory.getLogger(HandlerMethodResolver.class); |
52 | 50 | private final TypeResolver typeResolver; |
53 | 51 |
|
54 | 52 | public HandlerMethodResolver(TypeResolver typeResolver) { |
55 | 53 | this.typeResolver = typeResolver; |
56 | 54 | } |
57 | 55 |
|
58 | | - public List<ResolvedMethodParameter> methodParameters(final HandlerMethod methodToResolve) { |
59 | | - Class hostClass = HandlerMethodReturnTypes.useType(methodToResolve.getBeanType()) |
60 | | - .or(methodToResolve.getMethod().getDeclaringClass()); |
61 | | - ResolvedMethod resolvedMethod = getResolvedMethod(methodToResolve.getMethod(), hostClass); |
62 | | - List<ResolvedMethodParameter> parameters = newArrayList(); |
63 | | - MethodParameter[] methodParameters = methodToResolve.getMethodParameters(); |
64 | | - if (resolvedMethod != null) { |
65 | | - if (methodParameters.length == resolvedMethod.getArgumentCount()) { |
66 | | - for (int index = 0; index < resolvedMethod.getArgumentCount(); index++) { |
67 | | - MethodParameter methodParameter = methodParameters[index]; |
68 | | - methodParameter.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer()); |
69 | | - parameters.add(new ResolvedMethodParameter(methodParameter, resolvedMethod.getArgumentType(index))); |
70 | | - } |
71 | | - } else { |
72 | | - log.warn(String.format("Problem trying to resolve a method named %s", methodToResolve.getMethod().getName())); |
73 | | - log.warn(String.format("Method parameter count %s does not match resolved method argument count %s", |
74 | | - methodParameters.length, resolvedMethod.getArgumentCount())); |
75 | | - } |
76 | | - } |
77 | | - return parameters; |
| 56 | + public ResolvedType methodReturnType(HandlerMethod handlerMethod) { |
| 57 | + return resolvedMethod(handlerMethod).transform(toReturnType(typeResolver)).or(typeResolver.resolve(Void.TYPE)); |
78 | 58 | } |
79 | 59 |
|
80 | | - /** |
81 | | - * Resolves the return type of the given method in the class. |
82 | | - * |
83 | | - * @param methodToResolve a method which is declared in the implementing class or one of its subclasses |
84 | | - * @param actualClass the actual class. Used to resolve generic types if needed. |
85 | | - * @return |
86 | | - */ |
87 | | - public ResolvedType methodReturnType(final Method methodToResolve, Class<?> actualClass) { |
88 | | - ResolvedMethod resolvedMethod = getResolvedMethod(methodToResolve, actualClass); |
89 | | - if (resolvedMethod != null) { |
90 | | - return returnTypeOrVoid(resolvedMethod); |
| 60 | + public Optional<ResolvedMethod> resolvedMethod(HandlerMethod handlerMethod) { |
| 61 | + if (handlerMethod == null) { |
| 62 | + return Optional.absent(); |
91 | 63 | } |
92 | | - return typeResolver.resolve(methodToResolve.getReturnType()); |
| 64 | + Class hostClass = useType(handlerMethod.getBeanType()) |
| 65 | + .or(handlerMethod.getMethod().getDeclaringClass()); |
| 66 | + ResolvedType beanType = typeResolver.resolve(hostClass); |
| 67 | + ResolvedMethod[] memberMethods = new MemberResolver(typeResolver).resolve(beanType, null, null).getMemberMethods(); |
| 68 | + return from(newArrayList(memberMethods)) |
| 69 | + .filter(methodsMatches(handlerMethod.getMethod())) |
| 70 | + .first(); |
93 | 71 | } |
94 | 72 |
|
95 | | - private static Predicate<ResolvedMethod> methodNamesAreSame(final Method methodToResolve) { |
96 | | - return new Predicate<ResolvedMethod>() { |
| 73 | + private static Function<ResolvedMethod, ResolvedType> toReturnType(final TypeResolver resolver) { |
| 74 | + return new Function<ResolvedMethod, ResolvedType>() { |
97 | 75 | @Override |
98 | | - public boolean apply(ResolvedMethod input) { |
99 | | - return input.getRawMember().getName().equals(methodToResolve.getName()); |
| 76 | + public ResolvedType apply(ResolvedMethod input) { |
| 77 | + return fromNullable(input.getReturnType()).or(resolver.resolve(Void.TYPE)); |
100 | 78 | } |
101 | 79 | }; |
102 | 80 | } |
103 | 81 |
|
104 | | - @VisibleForTesting |
105 | | - static Ordering<ResolvedMethod> byArgumentCount() { |
106 | | - return Ordering.from(new Comparator<ResolvedMethod>() { |
107 | | - @Override |
108 | | - public int compare(ResolvedMethod first, ResolvedMethod second) { |
109 | | - return Ints.compare(first.getArgumentCount(), second.getArgumentCount()); |
110 | | - } |
111 | | - }); |
112 | | - } |
113 | | - |
114 | | - private static Iterable<ResolvedMethod> methodsWithSameNumberOfParams(Iterable<ResolvedMethod> filtered, |
115 | | - final Method methodToResolve) { |
116 | | - |
117 | | - return filter(filtered, new Predicate<ResolvedMethod>() { |
| 82 | + private static Predicate<ResolvedMethod> methodsMatches(final Method method) { |
| 83 | + return new Predicate<ResolvedMethod>() { |
118 | 84 | @Override |
119 | 85 | public boolean apply(ResolvedMethod input) { |
120 | | - return input.getArgumentCount() == methodToResolve.getParameterTypes().length; |
| 86 | + return input.getRawMember().equals(method); |
121 | 87 | } |
122 | | - }); |
| 88 | + }; |
123 | 89 | } |
124 | 90 |
|
125 | | - @VisibleForTesting |
126 | | - ResolvedMethod getResolvedMethod(final Method methodToResolve, Class<?> beanType) { |
127 | | - ResolvedType enclosingType = typeResolver.resolve(beanType); |
128 | | - MemberResolver resolver = new MemberResolver(typeResolver); |
129 | | - resolver.setIncludeLangObject(false); |
130 | | - ResolvedTypeWithMembers typeWithMembers = resolver.resolve(enclosingType, null, null); |
131 | | - Iterable<ResolvedMethod> filtered = filter(newArrayList(typeWithMembers.getMemberMethods()), |
132 | | - methodNamesAreSame(methodToResolve)); |
133 | | - return resolveToMethodWithMaxResolvedTypes(filtered, methodToResolve); |
134 | | - } |
135 | 91 |
|
136 | | - private ResolvedMethod resolveToMethodWithMaxResolvedTypes(Iterable<ResolvedMethod> filtered, |
137 | | - Method methodToResolve) { |
138 | | - |
139 | | - if (Iterables.size(filtered) > 1) { |
140 | | - Iterable<ResolvedMethod> covariantMethods = covariantMethods(filtered, methodToResolve); |
141 | | - if (Iterables.size(covariantMethods) == 0) { |
142 | | - return byArgumentCount().max(filtered); |
143 | | - } else if (Iterables.size(covariantMethods) == 1) { |
144 | | - return Iterables.getFirst(covariantMethods, null); |
145 | | - } else { |
146 | | - return byArgumentCount().max(covariantMethods); |
147 | | - } |
| 92 | + public static Optional<Class> useType(Class beanType) { |
| 93 | + if (Proxy.class.isAssignableFrom(beanType)) { |
| 94 | + return Optional.absent(); |
| 95 | + } |
| 96 | + if (Class.class.getName().equals(beanType.getName())) { |
| 97 | + return Optional.absent(); |
148 | 98 | } |
149 | | - return Iterables.getFirst(filtered, null); |
| 99 | + return fromNullable(beanType); |
150 | 100 | } |
151 | 101 |
|
152 | | - private Iterable<ResolvedMethod> covariantMethods(Iterable<ResolvedMethod> filtered, |
153 | | - final Method methodToResolve) { |
154 | | - |
155 | | - return filter(methodsWithSameNumberOfParams(filtered, methodToResolve), onlyCovariantMethods(methodToResolve)); |
| 102 | + public List<ResolvedMethodParameter> methodParameters(final HandlerMethod methodToResolve) { |
| 103 | + return resolvedMethod(methodToResolve) |
| 104 | + .transform(toParameters(methodToResolve)) |
| 105 | + .or(Lists.<ResolvedMethodParameter>newArrayList()); |
156 | 106 | } |
157 | 107 |
|
158 | | - private Predicate<ResolvedMethod> onlyCovariantMethods(final Method methodToResolve) { |
159 | | - return new Predicate<ResolvedMethod>() { |
| 108 | + private Function<ResolvedMethod, List<ResolvedMethodParameter>> toParameters(final HandlerMethod methodToResolve) { |
| 109 | + return new Function<ResolvedMethod, List<ResolvedMethodParameter>>() { |
160 | 110 | @Override |
161 | | - public boolean apply(ResolvedMethod input) { |
162 | | - for (int index = 0; index < input.getArgumentCount(); index++) { |
163 | | - if (!covariant(input.getArgumentType(index), methodToResolve.getGenericParameterTypes()[index])) { |
164 | | - return false; |
165 | | - } |
| 111 | + public List<ResolvedMethodParameter> apply(ResolvedMethod input) { |
| 112 | + List<ResolvedMethodParameter> parameters = newArrayList(); |
| 113 | + MethodParameter[] methodParameters = methodToResolve.getMethodParameters(); |
| 114 | + for (int i = 0; i < input.getArgumentCount(); i++) { |
| 115 | + parameters.add(new ResolvedMethodParameter(methodParameters[i], input.getArgumentType(i))); |
166 | 116 | } |
167 | | - ResolvedType candidateMethodReturnValue = returnTypeOrVoid(input); |
168 | | - return bothAreVoids(candidateMethodReturnValue, methodToResolve.getGenericReturnType()) |
169 | | - || contravariant(candidateMethodReturnValue, methodToResolve.getGenericReturnType()); |
| 117 | + return parameters; |
170 | 118 | } |
171 | 119 | }; |
172 | 120 | } |
173 | 121 |
|
174 | 122 | @VisibleForTesting |
175 | | - boolean bothAreVoids(ResolvedType candidateMethodReturnValue, Type returnType) { |
176 | | - return (Void.class == candidateMethodReturnValue.getErasedType() |
177 | | - || Void.TYPE == candidateMethodReturnValue.getErasedType()) |
178 | | - && (Void.TYPE == returnType |
179 | | - || Void.class == returnType); |
180 | | - } |
181 | | - |
182 | | - private ResolvedType returnTypeOrVoid(ResolvedMethod input) { |
183 | | - ResolvedType returnType = input.getReturnType(); |
184 | | - if (returnType == null) { |
185 | | - returnType = typeResolver.resolve(Void.class); |
186 | | - } |
187 | | - return returnType; |
| 123 | + static Ordering<ResolvedMethod> byArgumentCount() { |
| 124 | + return Ordering.from(new Comparator<ResolvedMethod>() { |
| 125 | + @Override |
| 126 | + public int compare(ResolvedMethod first, ResolvedMethod second) { |
| 127 | + return Ints.compare(first.getArgumentCount(), second.getArgumentCount()); |
| 128 | + } |
| 129 | + }); |
188 | 130 | } |
189 | 131 |
|
190 | | - boolean contravariant(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) { |
191 | | - return isSubClass(candidateMethodReturnValue, returnValueOnMethod) |
192 | | - || isGenericTypeSubclass(candidateMethodReturnValue, returnValueOnMethod); |
| 132 | + @VisibleForTesting |
| 133 | + boolean bothAreVoids(ResolvedType candidateMethodReturnValue, Type returnType) { |
| 134 | + return (Void.class == candidateMethodReturnValue.getErasedType() |
| 135 | + || Void.TYPE == candidateMethodReturnValue.getErasedType()) |
| 136 | + && (Void.TYPE == returnType |
| 137 | + || Void.class == returnType); |
193 | 138 | } |
194 | 139 |
|
195 | 140 | @VisibleForTesting |
196 | 141 | boolean isGenericTypeSubclass(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) { |
197 | 142 | return returnValueOnMethod instanceof ParameterizedType && |
198 | | - candidateMethodReturnValue.getErasedType() |
199 | | - .isAssignableFrom((Class<?>) ((ParameterizedType) returnValueOnMethod).getRawType()); |
| 143 | + candidateMethodReturnValue.getErasedType() |
| 144 | + .isAssignableFrom((Class<?>) ((ParameterizedType) returnValueOnMethod).getRawType()); |
200 | 145 | } |
201 | 146 |
|
202 | 147 | @VisibleForTesting |
203 | 148 | boolean isSubClass(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) { |
204 | 149 | return returnValueOnMethod instanceof Class |
205 | | - && candidateMethodReturnValue.getErasedType().isAssignableFrom((Class<?>) returnValueOnMethod); |
| 150 | + && candidateMethodReturnValue.getErasedType().isAssignableFrom((Class<?>) returnValueOnMethod); |
206 | 151 | } |
207 | 152 |
|
208 | 153 | @VisibleForTesting |
209 | 154 | boolean covariant(ResolvedType candidateMethodArgument, Type argumentOnMethod) { |
210 | 155 | return isSuperClass(candidateMethodArgument, argumentOnMethod) |
211 | | - || isGenericTypeSuperClass(candidateMethodArgument, argumentOnMethod); |
| 156 | + || isGenericTypeSuperClass(candidateMethodArgument, argumentOnMethod); |
212 | 157 | } |
213 | 158 |
|
214 | 159 | @VisibleForTesting |
215 | 160 | boolean isGenericTypeSuperClass(ResolvedType candidateMethodArgument, Type argumentOnMethod) { |
216 | 161 | return argumentOnMethod instanceof ParameterizedType && |
217 | | - ((Class<?>) ((ParameterizedType) argumentOnMethod).getRawType()) |
218 | | - .isAssignableFrom(candidateMethodArgument.getErasedType()); |
| 162 | + ((Class<?>) ((ParameterizedType) argumentOnMethod).getRawType()) |
| 163 | + .isAssignableFrom(candidateMethodArgument.getErasedType()); |
219 | 164 | } |
220 | 165 |
|
221 | 166 | @VisibleForTesting |
222 | 167 | boolean isSuperClass(ResolvedType candidateMethodArgument, Type argumentOnMethod) { |
223 | 168 | return argumentOnMethod instanceof Class |
224 | | - && ((Class<?>) argumentOnMethod).isAssignableFrom(candidateMethodArgument.getErasedType()); |
| 169 | + && ((Class<?>) argumentOnMethod).isAssignableFrom(candidateMethodArgument.getErasedType()); |
225 | 170 | } |
226 | 171 | } |
0 commit comments