Skip to content

Commit 49c3c84

Browse files
committed
Merge branch 'refactor/1241/method-overload-resolution'
fixes springfox#1241
2 parents 495ebfa + 58197af commit 49c3c84

4 files changed

Lines changed: 135 additions & 81 deletions

File tree

springfox-spring-web/src/main/java/springfox/documentation/spring/web/readers/operation/HandlerMethodResolver.java

Lines changed: 92 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,78 @@ public ResolvedType methodReturnType(HandlerMethod handlerMethod) {
6060
return resolvedMethod(handlerMethod).transform(toReturnType(typeResolver)).or(typeResolver.resolve(Void.TYPE));
6161
}
6262

63-
public Optional<ResolvedMethod> resolvedMethod(HandlerMethod handlerMethod) {
63+
public static Optional<Class> useType(Class beanType) {
64+
if (Proxy.class.isAssignableFrom(beanType)) {
65+
return Optional.absent();
66+
}
67+
if (Class.class.getName().equals(beanType.getName())) {
68+
return Optional.absent();
69+
}
70+
return fromNullable(beanType);
71+
}
72+
73+
public List<ResolvedMethodParameter> methodParameters(final HandlerMethod methodToResolve) {
74+
return resolvedMethod(methodToResolve)
75+
.transform(toParameters(methodToResolve))
76+
.or(Lists.<ResolvedMethodParameter>newArrayList());
77+
}
78+
79+
boolean contravariant(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) {
80+
return isSubClass(candidateMethodReturnValue, returnValueOnMethod)
81+
|| isGenericTypeSubclass(candidateMethodReturnValue, returnValueOnMethod);
82+
}
83+
84+
85+
@VisibleForTesting
86+
static Ordering<ResolvedMethod> byArgumentCount() {
87+
return Ordering.from(new Comparator<ResolvedMethod>() {
88+
@Override
89+
public int compare(ResolvedMethod first, ResolvedMethod second) {
90+
return Ints.compare(first.getArgumentCount(), second.getArgumentCount());
91+
}
92+
});
93+
}
94+
95+
@VisibleForTesting
96+
boolean bothAreVoids(ResolvedType candidateMethodReturnValue, Type returnType) {
97+
return (Void.class == candidateMethodReturnValue.getErasedType()
98+
|| Void.TYPE == candidateMethodReturnValue.getErasedType())
99+
&& (Void.TYPE == returnType
100+
|| Void.class == returnType);
101+
}
102+
@VisibleForTesting
103+
boolean isGenericTypeSubclass(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) {
104+
return returnValueOnMethod instanceof ParameterizedType &&
105+
candidateMethodReturnValue.getErasedType()
106+
.isAssignableFrom((Class<?>) ((ParameterizedType) returnValueOnMethod).getRawType());
107+
}
108+
109+
@VisibleForTesting
110+
boolean isSubClass(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) {
111+
return returnValueOnMethod instanceof Class
112+
&& candidateMethodReturnValue.getErasedType().isAssignableFrom((Class<?>) returnValueOnMethod);
113+
}
114+
115+
@VisibleForTesting
116+
boolean covariant(ResolvedType candidateMethodArgument, Type argumentOnMethod) {
117+
return isSuperClass(candidateMethodArgument, argumentOnMethod)
118+
|| isGenericTypeSuperClass(candidateMethodArgument, argumentOnMethod);
119+
}
120+
121+
@VisibleForTesting
122+
boolean isGenericTypeSuperClass(ResolvedType candidateMethodArgument, Type argumentOnMethod) {
123+
return argumentOnMethod instanceof ParameterizedType &&
124+
((Class<?>) ((ParameterizedType) argumentOnMethod).getRawType())
125+
.isAssignableFrom(candidateMethodArgument.getErasedType());
126+
}
127+
128+
@VisibleForTesting
129+
boolean isSuperClass(ResolvedType candidateMethodArgument, Type argumentOnMethod) {
130+
return argumentOnMethod instanceof Class
131+
&& ((Class<?>) argumentOnMethod).isAssignableFrom(candidateMethodArgument.getErasedType());
132+
}
133+
134+
private Optional<ResolvedMethod> resolvedMethod(HandlerMethod handlerMethod) {
64135
if (handlerMethod == null) {
65136
return Optional.absent();
66137
}
@@ -84,22 +155,6 @@ public ResolvedType apply(ResolvedMethod input) {
84155
};
85156
}
86157

87-
public static Optional<Class> useType(Class beanType) {
88-
if (Proxy.class.isAssignableFrom(beanType)) {
89-
return Optional.absent();
90-
}
91-
if (Class.class.getName().equals(beanType.getName())) {
92-
return Optional.absent();
93-
}
94-
return fromNullable(beanType);
95-
}
96-
97-
public List<ResolvedMethodParameter> methodParameters(final HandlerMethod methodToResolve) {
98-
return resolvedMethod(methodToResolve)
99-
.transform(toParameters(methodToResolve))
100-
.or(Lists.<ResolvedMethodParameter>newArrayList());
101-
}
102-
103158
private Function<ResolvedMethod, List<ResolvedMethodParameter>> toParameters(final HandlerMethod methodToResolve) {
104159
return new Function<ResolvedMethod, List<ResolvedMethodParameter>>() {
105160
@Override
@@ -114,26 +169,9 @@ public List<ResolvedMethodParameter> apply(ResolvedMethod input) {
114169
};
115170
}
116171

117-
@VisibleForTesting
118-
static Ordering<ResolvedMethod> byArgumentCount() {
119-
return Ordering.from(new Comparator<ResolvedMethod>() {
120-
@Override
121-
public int compare(ResolvedMethod first, ResolvedMethod second) {
122-
return Ints.compare(first.getArgumentCount(), second.getArgumentCount());
123-
}
124-
});
125-
}
126-
127-
@VisibleForTesting
128-
boolean bothAreVoids(ResolvedType candidateMethodReturnValue, Type returnType) {
129-
return (Void.class == candidateMethodReturnValue.getErasedType()
130-
|| Void.TYPE == candidateMethodReturnValue.getErasedType())
131-
&& (Void.TYPE == returnType
132-
|| Void.class == returnType);
133-
}
134-
135-
private static Iterable<ResolvedMethod> methodsWithSameNumberOfParams(Iterable<ResolvedMethod> filtered,
136-
final Method methodToResolve) {
172+
private static Iterable<ResolvedMethod> methodsWithSameNumberOfParams(
173+
Iterable<ResolvedMethod> filtered,
174+
final Method methodToResolve) {
137175

138176
return filter(filtered, new Predicate<ResolvedMethod>() {
139177
@Override
@@ -152,13 +190,14 @@ public boolean apply(ResolvedMethod input) {
152190
};
153191
}
154192

155-
private Optional<ResolvedMethod> resolveToMethodWithMaxResolvedTypes(Iterable<ResolvedMethod> filtered,
156-
Method methodToResolve) {
157-
193+
private Optional<ResolvedMethod> resolveToMethodWithMaxResolvedTypes(
194+
Iterable<ResolvedMethod> filtered,
195+
Method methodToResolve) {
158196
if (Iterables.size(filtered) > 1) {
159197
Iterable<ResolvedMethod> covariantMethods = covariantMethods(filtered, methodToResolve);
160198
if (Iterables.size(covariantMethods) == 0) {
161-
return Optional.of(byArgumentCount().max(filtered));
199+
return FluentIterable.from(filtered)
200+
.firstMatch(sameMethod(methodToResolve));
162201
} else if (Iterables.size(covariantMethods) == 1) {
163202
return FluentIterable.from(covariantMethods).first();
164203
} else {
@@ -168,8 +207,18 @@ private Optional<ResolvedMethod> resolveToMethodWithMaxResolvedTypes(Iterable<Re
168207
return FluentIterable.from(filtered).first();
169208
}
170209

171-
private Iterable<ResolvedMethod> covariantMethods(Iterable<ResolvedMethod> filtered,
172-
final Method methodToResolve) {
210+
private Predicate<ResolvedMethod> sameMethod(final Method methodToResolve) {
211+
return new Predicate<ResolvedMethod>() {
212+
@Override
213+
public boolean apply(ResolvedMethod input) {
214+
return methodToResolve.equals(input.getRawMember());
215+
}
216+
};
217+
}
218+
219+
private Iterable<ResolvedMethod> covariantMethods(
220+
Iterable<ResolvedMethod> filtered,
221+
final Method methodToResolve) {
173222

174223
return filter(methodsWithSameNumberOfParams(filtered, methodToResolve), onlyCovariantMethods(methodToResolve));
175224
}
@@ -197,42 +246,4 @@ private ResolvedType returnTypeOrVoid(ResolvedMethod input) {
197246
}
198247
return returnType;
199248
}
200-
201-
boolean contravariant(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) {
202-
return isSubClass(candidateMethodReturnValue, returnValueOnMethod)
203-
|| isGenericTypeSubclass(candidateMethodReturnValue, returnValueOnMethod);
204-
}
205-
206-
207-
@VisibleForTesting
208-
boolean isGenericTypeSubclass(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) {
209-
return returnValueOnMethod instanceof ParameterizedType &&
210-
candidateMethodReturnValue.getErasedType()
211-
.isAssignableFrom((Class<?>) ((ParameterizedType) returnValueOnMethod).getRawType());
212-
}
213-
214-
@VisibleForTesting
215-
boolean isSubClass(ResolvedType candidateMethodReturnValue, Type returnValueOnMethod) {
216-
return returnValueOnMethod instanceof Class
217-
&& candidateMethodReturnValue.getErasedType().isAssignableFrom((Class<?>) returnValueOnMethod);
218-
}
219-
220-
@VisibleForTesting
221-
boolean covariant(ResolvedType candidateMethodArgument, Type argumentOnMethod) {
222-
return isSuperClass(candidateMethodArgument, argumentOnMethod)
223-
|| isGenericTypeSuperClass(candidateMethodArgument, argumentOnMethod);
224-
}
225-
226-
@VisibleForTesting
227-
boolean isGenericTypeSuperClass(ResolvedType candidateMethodArgument, Type argumentOnMethod) {
228-
return argumentOnMethod instanceof ParameterizedType &&
229-
((Class<?>) ((ParameterizedType) argumentOnMethod).getRawType())
230-
.isAssignableFrom(candidateMethodArgument.getErasedType());
231-
}
232-
233-
@VisibleForTesting
234-
boolean isSuperClass(ResolvedType candidateMethodArgument, Type argumentOnMethod) {
235-
return argumentOnMethod instanceof Class
236-
&& ((Class<?>) argumentOnMethod).isAssignableFrom(candidateMethodArgument.getErasedType());
237-
}
238249
}

springfox-spring-web/src/test/groovy/springfox/documentation/spring/web/mixins/HandlerMethodsSupport.groovy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ trait HandlerMethodsSupport {
3232
new HandlerMethod(clazz, c.getMethod("methodToTest", Integer, DummyClass.Child))
3333
}
3434

35+
HandlerMethod loadDetailsWithOneParameter() {
36+
def clazz = new DummyClass.MethodResolutionToDemonstrate1241()
37+
Class c = clazz.getClass();
38+
new HandlerMethod(clazz, c.getMethod("loadDetails", String))
39+
}
40+
41+
HandlerMethod loadDetailsWithTwoParameter() {
42+
def clazz = new DummyClass.MethodResolutionToDemonstrate1241()
43+
Class c = clazz.getClass();
44+
new HandlerMethod(clazz, c.getMethod("loadDetails", String, Date))
45+
}
3546

3647
HandlerMethod unresolvableMethod() {
3748
null

springfox-spring-web/src/test/groovy/springfox/documentation/spring/web/readers/operation/HandlerMethodResolverSpec.groovy

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,23 @@ class HandlerMethodResolverSpec extends Specification implements HandlerMethodsS
5757
methodWithParent()| "ResponseEntity"| ["Integer", "Parent"]
5858
}
5959

60+
61+
def "Overloaded methods are resolved correctly" () {
62+
given:
63+
def sut = new HandlerMethodResolver(new TypeResolver())
64+
def resolvedParameters = sut.methodParameters(handlerMethod)
65+
.collect() { it.resolvedParameterType.getErasedType().simpleName }
66+
.sort()
67+
def resolvedReturnType = sut.methodReturnType(handlerMethod).erasedType.simpleName
68+
expect:
69+
parameters == resolvedParameters
70+
returnType == resolvedReturnType
71+
where:
72+
handlerMethod | returnType | parameters
73+
loadDetailsWithOneParameter() | "DTO[]" | ["String"]
74+
loadDetailsWithTwoParameter() | "DTO[]" | ["Date", "String"]
75+
}
76+
6077
def "When method was not resolvable calling methodParameters returns empty list" () {
6178
given:
6279
def sut = new HandlerMethodResolver(new TypeResolver()) {

springfox-spring-web/src/test/java/springfox/documentation/spring/web/dummy/DummyClass.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,21 @@ public ResponseEntity methodToTest(Integer integer, Parent child) {
376376

377377
public void methodToTest(Integer integer, Child child) {
378378
}
379+
380+
}
381+
382+
public class MethodResolutionToDemonstrate1241 {
383+
public DTO<String>[] loadDetails(String id) {
384+
return null;
385+
}
386+
387+
public DTO<String>[] loadDetails(String id, Date since) {
388+
return null;
389+
}
390+
}
391+
392+
public class DTO<T> {
393+
379394
}
380395

381396
class Parent {

0 commit comments

Comments
 (0)