|
36 | 36 |
|
37 | 37 | import javax.validation.constraints.NotNull; |
38 | 38 | import java.lang.reflect.AccessibleObject; |
| 39 | +import java.lang.reflect.AnnotatedElement; |
39 | 40 | import java.lang.reflect.AnnotatedType; |
40 | 41 | import java.lang.reflect.Constructor; |
41 | 42 | import java.lang.reflect.Field; |
|
46 | 47 | import java.util.Collections; |
47 | 48 | import java.util.Comparator; |
48 | 49 | import java.util.HashMap; |
| 50 | +import java.util.LinkedList; |
49 | 51 | import java.util.List; |
50 | 52 | import java.util.Map; |
51 | 53 | import java.util.Optional; |
@@ -172,26 +174,76 @@ public static GraphQLInterfaceType.Builder ifaceBuilder(Class<?> iface) throws G |
172 | 174 | return getInstance().getIfaceBuilder(iface); |
173 | 175 | } |
174 | 176 |
|
175 | | - private Class<?> getDeclaringClass(Method method) { |
176 | | - Class<?> object = method.getDeclaringClass(); |
177 | | - Class<?> declaringClass = object; |
178 | | - for (Class<?> iface : object.getInterfaces()) { |
| 177 | + private static Boolean isGraphQLField(AnnotatedElement element) { |
| 178 | + GraphQLField annotation = element.getAnnotation(GraphQLField.class); |
| 179 | + if (annotation == null) { |
| 180 | + return null; |
| 181 | + } |
| 182 | + return annotation.value(); |
| 183 | + } |
| 184 | + |
| 185 | + /** |
| 186 | + * breadthFirst parental ascent looking for closest method declaration with explicit annotation |
| 187 | + * |
| 188 | + * @param method The method to match |
| 189 | + * @return The closest GraphQLField annotation |
| 190 | + */ |
| 191 | + private boolean breadthFirstSearch(Method method) { |
| 192 | + final List<Class<?>> queue = new LinkedList<>(); |
| 193 | + final String methodName = method.getName(); |
| 194 | + final Class<?>[] parameterTypes = method.getParameterTypes(); |
| 195 | + queue.add(method.getDeclaringClass()); |
| 196 | + do { |
| 197 | + Class<?> cls = queue.remove(0); |
| 198 | + |
179 | 199 | try { |
180 | | - iface.getMethod(method.getName(), method.getParameterTypes()); |
181 | | - declaringClass = iface; |
| 200 | + method = cls.getDeclaredMethod(methodName, parameterTypes); |
| 201 | + Boolean gqf = isGraphQLField(method); |
| 202 | + if (gqf != null) { |
| 203 | + return gqf; |
| 204 | + } |
182 | 205 | } catch (NoSuchMethodException e) { |
183 | 206 | } |
184 | | - } |
185 | 207 |
|
186 | | - try { |
187 | | - if (object.getSuperclass() != null) { |
188 | | - object.getSuperclass().getMethod(method.getName(), method.getParameterTypes()); |
189 | | - declaringClass = object.getSuperclass(); |
| 208 | + Boolean gqf = isGraphQLField(cls); |
| 209 | + if (gqf != null) { |
| 210 | + return gqf; |
190 | 211 | } |
191 | | - } catch (NoSuchMethodException e) { |
192 | | - } |
193 | | - return declaringClass; |
194 | 212 |
|
| 213 | + // add interfaces to places to search |
| 214 | + for (Class<?> iface : cls.getInterfaces()) { |
| 215 | + queue.add(iface); |
| 216 | + } |
| 217 | + // add parent class to places to search |
| 218 | + Class<?> nxt = cls.getSuperclass(); |
| 219 | + if (nxt != null) { |
| 220 | + queue.add(nxt); |
| 221 | + } |
| 222 | + } while (!queue.isEmpty()); |
| 223 | + return false; |
| 224 | + } |
| 225 | + |
| 226 | + /** |
| 227 | + * direct parental ascent looking for closest declaration with explicit annotation |
| 228 | + * |
| 229 | + * @param field The field to find |
| 230 | + * @return The closest GraphQLField annotation |
| 231 | + */ |
| 232 | + private boolean parentalSearch(Field field) { |
| 233 | + Boolean gqf = isGraphQLField(field); |
| 234 | + if (gqf != null) { |
| 235 | + return gqf; |
| 236 | + } |
| 237 | + Class<?> cls = field.getDeclaringClass(); |
| 238 | + |
| 239 | + do { |
| 240 | + gqf = isGraphQLField(cls); |
| 241 | + if (gqf != null) { |
| 242 | + return gqf; |
| 243 | + } |
| 244 | + cls = cls.getSuperclass(); |
| 245 | + } while (cls != null); |
| 246 | + return false; |
195 | 247 | } |
196 | 248 |
|
197 | 249 | @Override |
@@ -228,28 +280,21 @@ public GraphQLObjectType.Builder getObjectBuilder(Class<?> object) throws GraphQ |
228 | 280 | if (description != null) { |
229 | 281 | builder.description(description.value()); |
230 | 282 | } |
231 | | - for (Method method : getOrderedMethods(object)) { |
232 | 283 |
|
233 | | - Class<?> declaringClass = getDeclaringClass(method); |
234 | | - |
235 | | - boolean valid = false; |
236 | | - try { |
237 | | - valid = (method.getAnnotation(GraphQLField.class) != null || |
238 | | - declaringClass.getMethod(method.getName(), method.getParameterTypes()).getAnnotation(GraphQLField.class) != null) |
239 | | - && !method.isBridge(); |
240 | | - } catch (NoSuchMethodException e) { |
241 | | - throw new GraphQLAnnotationsException("Unable to introspect method : " + method, e); |
| 284 | + for (Method method : getOrderedMethods(object)) { |
| 285 | + if(method.isBridge() || method.isSynthetic()) { |
| 286 | + continue; |
242 | 287 | } |
243 | | - |
244 | | - if (valid) { |
| 288 | + if (breadthFirstSearch(method)) { |
245 | 289 | builder.field(getField(method)); |
246 | 290 | } |
247 | 291 | } |
248 | 292 |
|
249 | 293 | for (Field field : getAllFields(object).values()) { |
250 | | - boolean valid = !Modifier.isStatic(field.getModifiers()) && |
251 | | - field.getAnnotation(GraphQLField.class) != null; |
252 | | - if (valid) { |
| 294 | + if(Modifier.isStatic(field.getModifiers())) { |
| 295 | + continue; |
| 296 | + } |
| 297 | + if (parentalSearch(field)) { |
253 | 298 | builder.field(getField(field)); |
254 | 299 | } |
255 | 300 | } |
|
0 commit comments