@@ -34,30 +34,53 @@ public interface Contract {
3434
3535 /**
3636 * Called to parse the methods in the class that are linked to HTTP requests.
37+ *
38+ * @param targetType {@link feign.Target#type() type} of the Feign interface.
3739 */
38- List <MethodMetadata > parseAndValidatateMetadata (Class <?> declaring );
40+ // TODO: break this and correct spelling at some point
41+ List <MethodMetadata > parseAndValidatateMetadata (Class <?> targetType );
3942
4043 abstract class BaseContract implements Contract {
4144
4245 @ Override
43- public List <MethodMetadata > parseAndValidatateMetadata (Class <?> declaring ) {
44- List <MethodMetadata > metadata = new ArrayList <MethodMetadata >();
45- for (Method method : declaring .getDeclaredMethods ()) {
46+ public List <MethodMetadata > parseAndValidatateMetadata (Class <?> targetType ) {
47+ checkState (targetType .getTypeParameters ().length == 0 , "Parameterized types unsupported: %s" ,
48+ targetType .getSimpleName ());
49+ checkState (targetType .getInterfaces ().length <= 1 , "Only single inheritance supported: %s" ,
50+ targetType .getSimpleName ());
51+ if (targetType .getInterfaces ().length == 1 ) {
52+ checkState (targetType .getInterfaces ()[0 ].getInterfaces ().length == 0 ,
53+ "Only single-level inheritance supported: %s" ,
54+ targetType .getSimpleName ());
55+ }
56+ Map <String , MethodMetadata > result = new LinkedHashMap <String , MethodMetadata >();
57+ for (Method method : targetType .getMethods ()) {
4658 if (method .getDeclaringClass () == Object .class ) {
4759 continue ;
4860 }
49- metadata .add (parseAndValidatateMetadata (method ));
61+ MethodMetadata metadata = parseAndValidateMetadata (targetType , method );
62+ checkState (!result .containsKey (metadata .configKey ()), "Overrides unsupported: %s" ,
63+ metadata .configKey ());
64+ result .put (metadata .configKey (), metadata );
5065 }
51- return metadata ;
66+ return new ArrayList < MethodMetadata >( result . values ()) ;
5267 }
5368
5469 /**
55- * Called indirectly by {@link #parseAndValidatateMetadata (Class)} .
70+ * @deprecated use {@link #parseAndValidateMetadata (Class, Method)} instead .
5671 */
72+ @ Deprecated
5773 public MethodMetadata parseAndValidatateMetadata (Method method ) {
74+ return parseAndValidateMetadata (method .getDeclaringClass (), method );
75+ }
76+
77+ /**
78+ * Called indirectly by {@link #parseAndValidatateMetadata(Class)}.
79+ */
80+ protected MethodMetadata parseAndValidateMetadata (Class <?> targetType , Method method ) {
5881 MethodMetadata data = new MethodMetadata ();
59- data .returnType (method .getGenericReturnType ());
60- data .configKey (Feign .configKey (method ));
82+ data .returnType (Types . resolve ( targetType , targetType , method .getGenericReturnType () ));
83+ data .configKey (Feign .configKey (targetType , method ));
6184
6285 for (Annotation methodAnnotation : method .getAnnotations ()) {
6386 processAnnotationOnMethod (data , methodAnnotation , method );
@@ -81,7 +104,7 @@ public MethodMetadata parseAndValidatateMetadata(Method method) {
81104 "Body parameters cannot be used with form parameters." );
82105 checkState (data .bodyIndex () == null , "Method has too many Body parameters: %s" , method );
83106 data .bodyIndex (i );
84- data .bodyType (method .getGenericParameterTypes ()[i ]);
107+ data .bodyType (Types . resolve ( targetType , targetType , method .getGenericParameterTypes ()[i ]) );
85108 }
86109 }
87110 return data ;
@@ -131,18 +154,25 @@ protected void nameParam(MethodMetadata data, String name, int i) {
131154 class Default extends BaseContract {
132155
133156 @ Override
134- public MethodMetadata parseAndValidatateMetadata (Method method ) {
135- MethodMetadata data = super .parseAndValidatateMetadata (method );
136- if (method .getDeclaringClass ().isAnnotationPresent (Headers .class )) {
137- String [] headersOnType = method .getDeclaringClass ().getAnnotation (Headers .class ).value ();
157+ protected MethodMetadata parseAndValidateMetadata (Class <?> targetType , Method method ) {
158+ MethodMetadata data = super .parseAndValidateMetadata (targetType , method );
159+ headersFromAnnotation (method .getDeclaringClass (), data );
160+ if (method .getDeclaringClass () != targetType ) {
161+ headersFromAnnotation (targetType , data );
162+ }
163+ return data ;
164+ }
165+
166+ private void headersFromAnnotation (Class <?> targetType , MethodMetadata data ) {
167+ if (targetType .isAnnotationPresent (Headers .class )) {
168+ String [] headersOnType = targetType .getAnnotation (Headers .class ).value ();
138169 checkState (headersOnType .length > 0 , "Headers annotation was empty on type %s." ,
139- method . getDeclaringClass () .getName ());
170+ targetType .getName ());
140171 Map <String , Collection <String >> headers = toMap (headersOnType );
141172 headers .putAll (data .template ().headers ());
142173 data .template ().headers (null ); // to clear
143174 data .template ().headers (headers );
144175 }
145- return data ;
146176 }
147177
148178 @ Override
0 commit comments