|
31 | 31 | /** |
32 | 32 | * Defines what annotations and values are valid on interfaces. |
33 | 33 | */ |
34 | | -public abstract class Contract { |
| 34 | +public interface Contract { |
35 | 35 |
|
36 | 36 | /** |
37 | 37 | * Called to parse the methods in the class that are linked to HTTP requests. |
38 | 38 | */ |
39 | | - public List<MethodMetadata> parseAndValidatateMetadata(Class<?> declaring) { |
40 | | - List<MethodMetadata> metadata = new ArrayList<MethodMetadata>(); |
41 | | - for (Method method : declaring.getDeclaredMethods()) { |
42 | | - if (method.getDeclaringClass() == Object.class) |
43 | | - continue; |
44 | | - metadata.add(parseAndValidatateMetadata(method)); |
45 | | - } |
46 | | - return metadata; |
47 | | - } |
| 39 | + List<MethodMetadata> parseAndValidatateMetadata(Class<?> declaring); |
48 | 40 |
|
49 | | - /** |
50 | | - * Called indirectly by {@link #parseAndValidatateMetadata(Class)}. |
51 | | - */ |
52 | | - public MethodMetadata parseAndValidatateMetadata(Method method) { |
53 | | - MethodMetadata data = new MethodMetadata(); |
54 | | - data.decodeInto(method.getGenericReturnType()); |
55 | | - data.configKey(Feign.configKey(method)); |
| 41 | + public static abstract class BaseContract implements Contract { |
56 | 42 |
|
57 | | - for (Annotation methodAnnotation : method.getAnnotations()) { |
58 | | - processAnnotationOnMethod(data, methodAnnotation, method); |
| 43 | + @Override public List<MethodMetadata> parseAndValidatateMetadata(Class<?> declaring) { |
| 44 | + List<MethodMetadata> metadata = new ArrayList<MethodMetadata>(); |
| 45 | + for (Method method : declaring.getDeclaredMethods()) { |
| 46 | + if (method.getDeclaringClass() == Object.class) |
| 47 | + continue; |
| 48 | + metadata.add(parseAndValidatateMetadata(method)); |
| 49 | + } |
| 50 | + return metadata; |
59 | 51 | } |
60 | | - checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)", |
61 | | - method.getName()); |
62 | | - Class<?>[] parameterTypes = method.getParameterTypes(); |
63 | 52 |
|
64 | | - Annotation[][] parameterAnnotations = method.getParameterAnnotations(); |
65 | | - int count = parameterAnnotations.length; |
66 | | - for (int i = 0; i < count; i++) { |
67 | | - boolean isHttpAnnotation = false; |
68 | | - if (parameterAnnotations[i] != null) { |
69 | | - isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i); |
| 53 | + /** |
| 54 | + * Called indirectly by {@link #parseAndValidatateMetadata(Class)}. |
| 55 | + */ |
| 56 | + public MethodMetadata parseAndValidatateMetadata(Method method) { |
| 57 | + MethodMetadata data = new MethodMetadata(); |
| 58 | + data.returnType(method.getGenericReturnType()); |
| 59 | + data.configKey(Feign.configKey(method)); |
| 60 | + |
| 61 | + if (Observable.class.isAssignableFrom(method.getReturnType())) { |
| 62 | + Type context = method.getGenericReturnType(); |
| 63 | + Type observableType = resolveLastTypeParameter(method.getGenericReturnType(), Observable.class); |
| 64 | + checkState(observableType != null, "Expected param %s to be Observable<X> or Observable<? super X> or a subtype", |
| 65 | + context, observableType); |
| 66 | + data.incrementalType(observableType); |
| 67 | + } |
| 68 | + |
| 69 | + for (Annotation methodAnnotation : method.getAnnotations()) { |
| 70 | + processAnnotationOnMethod(data, methodAnnotation, method); |
70 | 71 | } |
71 | | - if (parameterTypes[i] == URI.class) { |
72 | | - data.urlIndex(i); |
73 | | - } else if (IncrementalCallback.class.isAssignableFrom(parameterTypes[i])) { |
74 | | - checkState(method.getReturnType() == void.class, "IncrementalCallback methods must return void: %s", method); |
75 | | - checkState(i == count - 1, "IncrementalCallback must be the last parameter: %s", method); |
76 | | - Type context = method.getGenericParameterTypes()[i]; |
77 | | - Type incrementalCallbackType = resolveLastTypeParameter(context, IncrementalCallback.class); |
78 | | - data.decodeInto(incrementalCallbackType); |
79 | | - data.incrementalCallbackIndex(i); |
80 | | - checkState(incrementalCallbackType != null, "Expected param %s to be IncrementalCallback<X> or IncrementalCallback<? super X> or a subtype", |
81 | | - context, incrementalCallbackType); |
82 | | - } else if (!isHttpAnnotation) { |
83 | | - checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters."); |
84 | | - checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method); |
85 | | - data.bodyIndex(i); |
86 | | - data.bodyType(method.getGenericParameterTypes()[i]); |
| 72 | + checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)", |
| 73 | + method.getName()); |
| 74 | + Class<?>[] parameterTypes = method.getParameterTypes(); |
| 75 | + |
| 76 | + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); |
| 77 | + int count = parameterAnnotations.length; |
| 78 | + for (int i = 0; i < count; i++) { |
| 79 | + boolean isHttpAnnotation = false; |
| 80 | + if (parameterAnnotations[i] != null) { |
| 81 | + isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i); |
| 82 | + } |
| 83 | + if (parameterTypes[i] == URI.class) { |
| 84 | + data.urlIndex(i); |
| 85 | + } else if (!isHttpAnnotation) { |
| 86 | + checkState(!Observer.class.isAssignableFrom(parameterTypes[i]), |
| 87 | + "Please return Observer as opposed to passing an Observable arg: %s", method); |
| 88 | + checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters."); |
| 89 | + checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method); |
| 90 | + data.bodyIndex(i); |
| 91 | + data.bodyType(method.getGenericParameterTypes()[i]); |
| 92 | + } |
87 | 93 | } |
| 94 | + return data; |
88 | 95 | } |
89 | | - return data; |
90 | | - } |
91 | 96 |
|
92 | | - /** |
93 | | - * @param data metadata collected so far relating to the current java method. |
94 | | - * @param annotation annotations present on the current method annotation. |
95 | | - * @param method method currently being processed. |
96 | | - */ |
97 | | - protected abstract void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method); |
| 97 | + /** |
| 98 | + * @param data metadata collected so far relating to the current java method. |
| 99 | + * @param annotation annotations present on the current method annotation. |
| 100 | + * @param method method currently being processed. |
| 101 | + */ |
| 102 | + protected abstract void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method); |
98 | 103 |
|
99 | | - /** |
100 | | - * @param data metadata collected so far relating to the current java method. |
101 | | - * @param annotations annotations present on the current parameter annotation. |
102 | | - * @param paramIndex if you find a name in {@code annotations}, call {@link #nameParam(MethodMetadata, String, |
103 | | - * int)} with this as the last parameter. |
104 | | - * @return true if you called {@link #nameParam(MethodMetadata, String, int)} after finding an http-relevant |
105 | | - * annotation. |
106 | | - */ |
107 | | - protected abstract boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex); |
| 104 | + /** |
| 105 | + * @param data metadata collected so far relating to the current java method. |
| 106 | + * @param annotations annotations present on the current parameter annotation. |
| 107 | + * @param paramIndex if you find a name in {@code annotations}, call {@link #nameParam(MethodMetadata, String, |
| 108 | + * int)} with this as the last parameter. |
| 109 | + * @return true if you called {@link #nameParam(MethodMetadata, String, int)} after finding an http-relevant |
| 110 | + * annotation. |
| 111 | + */ |
| 112 | + protected abstract boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex); |
108 | 113 |
|
109 | 114 |
|
110 | | - protected Collection<String> addTemplatedParam(Collection<String> possiblyNull, String name) { |
111 | | - if (possiblyNull == null) |
112 | | - possiblyNull = new ArrayList<String>(); |
113 | | - possiblyNull.add(String.format("{%s}", name)); |
114 | | - return possiblyNull; |
115 | | - } |
| 115 | + protected Collection<String> addTemplatedParam(Collection<String> possiblyNull, String name) { |
| 116 | + if (possiblyNull == null) |
| 117 | + possiblyNull = new ArrayList<String>(); |
| 118 | + possiblyNull.add(String.format("{%s}", name)); |
| 119 | + return possiblyNull; |
| 120 | + } |
116 | 121 |
|
117 | | - /** |
118 | | - * links a parameter name to its index in the method signature. |
119 | | - */ |
120 | | - protected void nameParam(MethodMetadata data, String name, int i) { |
121 | | - Collection<String> names = data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList<String>(); |
122 | | - names.add(name); |
123 | | - data.indexToName().put(i, names); |
| 122 | + /** |
| 123 | + * links a parameter name to its index in the method signature. |
| 124 | + */ |
| 125 | + protected void nameParam(MethodMetadata data, String name, int i) { |
| 126 | + Collection<String> names = data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList<String>(); |
| 127 | + names.add(name); |
| 128 | + data.indexToName().put(i, names); |
| 129 | + } |
124 | 130 | } |
125 | 131 |
|
126 | | - static class Default extends Contract { |
| 132 | + static class Default extends BaseContract { |
127 | 133 |
|
128 | 134 | @Override |
129 | 135 | protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) { |
|
0 commit comments