1313 */
1414package feign ;
1515
16- import static feign .Util .checkState ;
17- import static feign .Util .emptyToNull ;
1816import java .lang .annotation .Annotation ;
1917import java .lang .reflect .*;
2018import java .net .URI ;
2119import java .util .*;
2220import java .util .regex .Matcher ;
2321import java .util .regex .Pattern ;
2422import feign .Request .HttpMethod ;
23+ import static feign .Util .*;
2524
2625/**
2726 * Defines what annotations and values are valid on interfaces.
@@ -43,15 +42,7 @@ abstract class BaseContract implements Contract {
4342 */
4443 @ Override
4544 public List <MethodMetadata > parseAndValidateMetadata (Class <?> targetType ) {
46- checkState (targetType .getTypeParameters ().length == 0 , "Parameterized types unsupported: %s" ,
47- targetType .getSimpleName ());
48- checkState (targetType .getInterfaces ().length <= 1 , "Only single inheritance supported: %s" ,
49- targetType .getSimpleName ());
50- if (targetType .getInterfaces ().length == 1 ) {
51- checkState (targetType .getInterfaces ()[0 ].getInterfaces ().length == 0 ,
52- "Only single-level inheritance supported: %s" ,
53- targetType .getSimpleName ());
54- }
45+ validateTargetType (targetType );
5546 final Map <String , MethodMetadata > result = new LinkedHashMap <String , MethodMetadata >();
5647 for (final Method method : targetType .getMethods ()) {
5748 if (method .getDeclaringClass () == Object .class ||
@@ -60,8 +51,20 @@ public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
6051 continue ;
6152 }
6253 final MethodMetadata metadata = parseAndValidateMetadata (targetType , method );
63- checkState (!result .containsKey (metadata .configKey ()), "Overrides unsupported: %s" ,
64- metadata .configKey ());
54+ if (result .containsKey (metadata .configKey ())) {
55+ // there are two different scenarios here: getMethods() returns the overridden method
56+ // prior
57+ // to the inherited generic one or vice versa, e.g.
58+ // java.lang.Object get(...) may come before of after java.lang.String get(...)
59+ if (metadata .returnType ().equals (Object .class )) {
60+ // ignore generic override
61+ continue ;
62+ } else if (result .get (metadata .configKey ()).returnType ().equals (Object .class )) {
63+ // replace generic override
64+ result .put (metadata .configKey (), metadata );
65+ continue ;
66+ }
67+ }
6568 result .put (metadata .configKey (), metadata );
6669 }
6770 return new ArrayList <>(result .values ());
@@ -146,6 +149,84 @@ protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method me
146149 return data ;
147150 }
148151
152+ private void validateTargetType (Class <?> targetType ) {
153+ checkState (targetType .getTypeParameters ().length == 0 , "Parameterized types unsupported: %s" ,
154+ targetType .getSimpleName ());
155+ checkState (targetType .getInterfaces ().length <= 1 , "Only single inheritance supported: %s" ,
156+ targetType .getSimpleName ());
157+ // original contract: single level, single inheritance
158+ if (targetType .getInterfaces ().length == 0
159+ || targetType .getInterfaces ()[0 ].getInterfaces ().length == 0 ) {
160+ return ;
161+ }
162+
163+ // recursively collect all inherited interfaces starting from superType and
164+ // validate these for offending annotations
165+ Class <?> superType = targetType .getInterfaces ()[0 ];
166+
167+ Stack <Class <?>> candidateTypes = new Stack <>();
168+ List <Class <?>> inheritedTypes = new ArrayList <>();
169+
170+ candidateTypes .push (superType );
171+ while (!candidateTypes .empty ()) {
172+ Class <?> inheritedType = candidateTypes .pop ();
173+ for (Class <?> type : inheritedType .getInterfaces ()) {
174+ candidateTypes .push (type );
175+ }
176+ if (superType .equals (inheritedType )) {
177+ continue ;
178+ }
179+ inheritedTypes .add (inheritedType );
180+ }
181+
182+ if (!inheritedTypes .isEmpty ()) {
183+ for (Class <?> inheritedType : inheritedTypes ) {
184+ validateInheritedType (targetType , inheritedType );
185+ }
186+ }
187+ }
188+
189+ private void validateInheritedType (Class <?> targetType , Class <?> inheritedType ) {
190+ for (Class <? extends Annotation > annotationClass : getNonInheritableTypeAnnotations ()) {
191+ checkState (inheritedType .getAnnotationsByType (annotationClass ).length == 0 ,
192+ "Multiple inheritance supported for plain types only: targetType: %s, inheritedType: %s, not inheritable: %s" ,
193+ targetType .getSimpleName (), inheritedType .getSimpleName (),
194+ annotationClass .getSimpleName ());
195+ validateInheritedMethods (targetType , inheritedType );
196+ }
197+ }
198+
199+ private void validateInheritedMethods (Class <?> targetType , Class <?> inheritedType ) {
200+ for (Method method : inheritedType .getDeclaredMethods ()) {
201+ for (Class <? extends Annotation > annotationClass : getNonInheritableMethodAnnotations ()) {
202+ checkState (method .getAnnotationsByType (annotationClass ).length == 0 ,
203+ "Multiple inheritance supported for plain types only: targetType: %s, inheritedType: %s, method: %s, not inheritable: %s" ,
204+ targetType .getSimpleName (), inheritedType .getSimpleName (), method .getName (),
205+ annotationClass .getSimpleName ());
206+ validateInheritedMethodParameters (targetType , inheritedType , method );
207+ }
208+ }
209+ }
210+
211+ private void validateInheritedMethodParameters (Class <?> targetType ,
212+ Class <?> inheritedType ,
213+ Method method ) {
214+ for (Parameter parameter : method .getParameters ()) {
215+ for (Class <? extends Annotation > annotationClass : getNonInheritableParameterAnnotations ()) {
216+ checkState (parameter .getAnnotationsByType (annotationClass ).length == 0 ,
217+ "Multiple inheritance supported for plain types only: targetType: %s, inheritedType: %s, method: %s, parameter: %s, not inheritable: %s" ,
218+ targetType .getSimpleName (), inheritedType .getSimpleName (), method .getName (),
219+ parameter .getName (), annotationClass .getSimpleName ());
220+ }
221+ }
222+ }
223+
224+ protected abstract List <Class <? extends Annotation >> getNonInheritableTypeAnnotations ();
225+
226+ protected abstract List <Class <? extends Annotation >> getNonInheritableMethodAnnotations ();
227+
228+ protected abstract List <Class <? extends Annotation >> getNonInheritableParameterAnnotations ();
229+
149230 private static void checkMapString (String name , Class <?> type , Type genericType ) {
150231 checkState (Map .class .isAssignableFrom (type ),
151232 "%s parameter must be a Map: %s" , name , type );
@@ -310,5 +391,19 @@ private static Map<String, Collection<String>> toMap(String[] input) {
310391 return result ;
311392 }
312393
394+ @ Override
395+ protected List <Class <? extends Annotation >> getNonInheritableTypeAnnotations () {
396+ return Collections .singletonList (Headers .class );
397+ }
398+
399+ @ Override
400+ protected List <Class <? extends Annotation >> getNonInheritableMethodAnnotations () {
401+ return Arrays .asList (Headers .class , RequestLine .class , Body .class );
402+ }
403+
404+ @ Override
405+ protected List <Class <? extends Annotation >> getNonInheritableParameterAnnotations () {
406+ return Arrays .asList (HeaderMap .class , Param .class , QueryMap .class );
407+ }
313408 }
314409}
0 commit comments