From ee063089ccdb39f2964e8889bc8c7c3228bbaa7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Frantz=C3=A9n?= Date: Mon, 26 Oct 2015 14:14:18 +0100 Subject: [PATCH 1/3] Issue 164 - Be able to mix fields with and without @ApiObjectField on an object marked with @ApiObject If you mark an object with @ApiObject, fields not marked with @ApiObjectField are silently ignored so you are forced to add @ApiObjectField to all fields. --- .../scanner/AbstractSpringJSONDocScanner.java | 3 + ...ixedFieldWithAndWIthoutAnnotationTest.java | 61 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/field/MixedFieldWithAndWIthoutAnnotationTest.java diff --git a/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/AbstractSpringJSONDocScanner.java b/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/AbstractSpringJSONDocScanner.java index f9483931..8ae6fec1 100644 --- a/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/AbstractSpringJSONDocScanner.java +++ b/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/AbstractSpringJSONDocScanner.java @@ -239,6 +239,9 @@ public ApiObjectDoc initApiObjectDoc(Class clazz) { public ApiObjectDoc mergeApiObjectDoc(Class clazz, ApiObjectDoc apiObjectDoc) { if(clazz.isAnnotationPresent(ApiObject.class)) { ApiObjectDoc jsondocApiObjectDoc = JSONDocApiObjectDocBuilder.build(clazz); + if (jsondocApiObjectDoc.getFields() != null) { + jsondocApiObjectDoc.getFields().addAll(apiObjectDoc.getFields()); + } BeanUtils.copyProperties(jsondocApiObjectDoc, apiObjectDoc); } return apiObjectDoc; diff --git a/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/field/MixedFieldWithAndWIthoutAnnotationTest.java b/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/field/MixedFieldWithAndWIthoutAnnotationTest.java new file mode 100644 index 00000000..22ec4dd5 --- /dev/null +++ b/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/field/MixedFieldWithAndWIthoutAnnotationTest.java @@ -0,0 +1,61 @@ +package org.jsondoc.spring4mvc.scanner.field; + + +import static org.springframework.web.bind.annotation.RequestMethod.GET; + +import java.util.Arrays; +import java.util.Set; + +import org.jsondoc.core.annotation.ApiObject; +import org.jsondoc.core.annotation.ApiObjectField; +import org.jsondoc.core.pojo.ApiObjectDoc; +import org.jsondoc.core.pojo.ApiObjectFieldDoc; +import org.jsondoc.core.pojo.JSONDoc; +import org.jsondoc.core.pojo.JSONDoc.MethodDisplay; +import org.jsondoc.springmvc.scanner.Spring4JSONDocScanner; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.google.common.collect.Lists; + +public class MixedFieldWithAndWIthoutAnnotationTest { + + @Test + public void testSpring4JSONDocScanner() { + Spring4JSONDocScanner jsondocScanner = new Spring4JSONDocScanner(); + JSONDoc jsonDoc = jsondocScanner.getJSONDoc("1.0", "/field", Lists.newArrayList("org.jsondoc.spring4mvc.scanner.field"), true, MethodDisplay.URI); + Assert.assertEquals(1, jsonDoc.getObjects().size()); + Set objects = jsonDoc.getObjects().get(""); + ApiObjectDoc o = objects.iterator().next(); + ApiObjectFieldDoc[] fields = o.getFields().toArray(new ApiObjectFieldDoc[]{}); + Arrays.sort(fields); + Assert.assertEquals(2, fields.length); + Assert.assertEquals("age", fields[0].getName()); + Assert.assertEquals("id", fields[1].getName()); + } + + @RestController + @RequestMapping(value="/spring4") + public class Spring4RestController { + @RequestMapping(value="/id-field/{customer-id}", method=GET) + public Spring4ObjectField getCustomer2(@PathVariable("customer-id") Long customerId) { + return new Spring4ObjectField(); + } + } + + @ApiObject(name="Spring4ObjectField", show=true) + public static class Spring4ObjectField { + + @ApiObjectField(description="Hello Dolly") + private String id; + + // Without @ApiObjectField annotation + private Long age; + + } +} + + From 600e4bf47a3a0641c6fd9b09d805ccfbfe82e395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Frantz=C3=A9n?= Date: Mon, 26 Oct 2015 15:44:45 +0100 Subject: [PATCH 2/3] Make it possible to add @ApiObjectField annotation on getters and setters #163 To be able to support Google AutoValue (https://github.com/google/auto/tree/master/value) it would be nice if the @ApiObjectField annotation cound be placed on getters and setters of the abstract object, as the fields themselves are not available to the restcontroller. --- .../core/annotation/ApiObjectField.java | 2 +- .../builder/JSONDocApiObjectDocBuilder.java | 25 ++++-- .../JSONDocApiObjectFieldDocBuilder.java | 27 ++++-- .../core/util/JSONDocFieldWrapper.java | 63 ++++++++++--- .../JSONDocHibernateValidatorProcessor.java | 4 +- .../core/util/JSONDocTemplateBuilder.java | 44 ++++++--- .../org/jsondoc/core/util/JSONDocUtils.java | 14 +++ .../scanner/AbstractSpringJSONDocScanner.java | 5 ++ .../scanner/builder/SpringObjectBuilder.java | 20 ++++- .../ApiObjectFieldAnnotationOnMethodTest.java | 89 +++++++++++++++++++ spring-boot-starter-jsondoc/.gitignore | 1 + 11 files changed, 250 insertions(+), 44 deletions(-) create mode 100644 jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/method/ApiObjectFieldAnnotationOnMethodTest.java diff --git a/jsondoc-core/src/main/java/org/jsondoc/core/annotation/ApiObjectField.java b/jsondoc-core/src/main/java/org/jsondoc/core/annotation/ApiObjectField.java index 9d5614b4..b7eb3a41 100644 --- a/jsondoc-core/src/main/java/org/jsondoc/core/annotation/ApiObjectField.java +++ b/jsondoc-core/src/main/java/org/jsondoc/core/annotation/ApiObjectField.java @@ -12,7 +12,7 @@ * */ @Documented -@Target(value = ElementType.FIELD) +@Target(value = { ElementType.FIELD, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface ApiObjectField { diff --git a/jsondoc-core/src/main/java/org/jsondoc/core/scanner/builder/JSONDocApiObjectDocBuilder.java b/jsondoc-core/src/main/java/org/jsondoc/core/scanner/builder/JSONDocApiObjectDocBuilder.java index 6b692f77..027c76f4 100644 --- a/jsondoc-core/src/main/java/org/jsondoc/core/scanner/builder/JSONDocApiObjectDocBuilder.java +++ b/jsondoc-core/src/main/java/org/jsondoc/core/scanner/builder/JSONDocApiObjectDocBuilder.java @@ -1,6 +1,7 @@ package org.jsondoc.core.scanner.builder; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.Set; import java.util.TreeSet; @@ -9,6 +10,7 @@ import org.jsondoc.core.pojo.ApiObjectDoc; import org.jsondoc.core.pojo.ApiObjectFieldDoc; import org.jsondoc.core.scanner.DefaultJSONDocScanner; +import org.jsondoc.core.util.JSONDocUtils; public class JSONDocApiObjectDocBuilder { @@ -17,14 +19,23 @@ public static ApiObjectDoc build(Class clazz) { ApiObjectDoc apiObjectDoc = new ApiObjectDoc(); Set fieldDocs = new TreeSet(); - for (Field field : clazz.getDeclaredFields()) { - if (field.getAnnotation(ApiObjectField.class) != null) { - ApiObjectFieldDoc fieldDoc = JSONDocApiObjectFieldDocBuilder.build(field.getAnnotation(ApiObjectField.class), field); - fieldDoc.setSupportedversions(JSONDocApiVersionDocBuilder.build(field)); - fieldDocs.add(fieldDoc); - } - } + for (Field field : clazz.getDeclaredFields()) { + if (field.getAnnotation(ApiObjectField.class) != null) { + ApiObjectFieldDoc fieldDoc = JSONDocApiObjectFieldDocBuilder.build(field.getAnnotation(ApiObjectField.class), field); + fieldDoc.setSupportedversions(JSONDocApiVersionDocBuilder.build(field)); + fieldDocs.add(fieldDoc); + } + } + + for (Method method : clazz.getDeclaredMethods()) { + if (JSONDocUtils.isFieldMethod(method)) { + ApiObjectFieldDoc fieldDoc = JSONDocApiObjectFieldDocBuilder.build(method.getAnnotation(ApiObjectField.class), method); + fieldDoc.setSupportedversions(JSONDocApiVersionDocBuilder.build(method)); + fieldDocs.add(fieldDoc); + } + } + Class c = clazz.getSuperclass(); if (c != null) { if (c.isAnnotationPresent(ApiObject.class)) { diff --git a/jsondoc-core/src/main/java/org/jsondoc/core/scanner/builder/JSONDocApiObjectFieldDocBuilder.java b/jsondoc-core/src/main/java/org/jsondoc/core/scanner/builder/JSONDocApiObjectFieldDocBuilder.java index 06752a78..1b5eb9ba 100644 --- a/jsondoc-core/src/main/java/org/jsondoc/core/scanner/builder/JSONDocApiObjectFieldDocBuilder.java +++ b/jsondoc-core/src/main/java/org/jsondoc/core/scanner/builder/JSONDocApiObjectFieldDocBuilder.java @@ -1,6 +1,8 @@ package org.jsondoc.core.scanner.builder; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; import org.jsondoc.core.annotation.ApiObjectField; import org.jsondoc.core.pojo.ApiObjectFieldDoc; @@ -8,21 +10,34 @@ import org.jsondoc.core.util.JSONDocHibernateValidatorProcessor; import org.jsondoc.core.util.JSONDocType; import org.jsondoc.core.util.JSONDocTypeBuilder; +import org.jsondoc.core.util.JSONDocUtils; public class JSONDocApiObjectFieldDocBuilder { - public static ApiObjectFieldDoc build(ApiObjectField annotation, Field field) { + public static ApiObjectFieldDoc build(ApiObjectField annotation, Field field) { + ApiObjectFieldDoc doc = build(annotation, field.getName(), field.getType(), field.getGenericType()); + JSONDocHibernateValidatorProcessor.processHibernateValidatorAnnotations(field, doc); + return doc; + } + + public static ApiObjectFieldDoc build(ApiObjectField annotation, Method method) { + ApiObjectFieldDoc doc = build(annotation, JSONDocUtils.getPropertyName(method), method.getReturnType(), method.getGenericReturnType()); + JSONDocHibernateValidatorProcessor.processHibernateValidatorAnnotations(method, doc); + return doc; + } + + private static ApiObjectFieldDoc build(ApiObjectField annotation, String name, Class type, Type genericType) { ApiObjectFieldDoc apiPojoFieldDoc = new ApiObjectFieldDoc(); if (!annotation.name().trim().isEmpty()) { apiPojoFieldDoc.setName(annotation.name()); } else { - apiPojoFieldDoc.setName(field.getName()); + apiPojoFieldDoc.setName(name); } apiPojoFieldDoc.setDescription(annotation.description()); - apiPojoFieldDoc.setJsondocType(JSONDocTypeBuilder.build(new JSONDocType(), field.getType(), field.getGenericType())); + apiPojoFieldDoc.setJsondocType(JSONDocTypeBuilder.build(new JSONDocType(), type, genericType)); // if allowedvalues property is populated on an enum field, then the enum values are overridden with the allowedvalues ones - if (field.getType().isEnum() && annotation.allowedvalues().length == 0) { - apiPojoFieldDoc.setAllowedvalues(DefaultJSONDocScanner.enumConstantsToStringArray(field.getType().getEnumConstants())); + if (type.isEnum() && annotation.allowedvalues().length == 0) { + apiPojoFieldDoc.setAllowedvalues(DefaultJSONDocScanner.enumConstantsToStringArray(type.getEnumConstants())); } else { apiPojoFieldDoc.setAllowedvalues(annotation.allowedvalues()); } @@ -33,8 +48,6 @@ public static ApiObjectFieldDoc build(ApiObjectField annotation, Field field) { apiPojoFieldDoc.addFormat(annotation.format()); } - JSONDocHibernateValidatorProcessor.processHibernateValidatorAnnotations(field, apiPojoFieldDoc); - return apiPojoFieldDoc; } diff --git a/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocFieldWrapper.java b/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocFieldWrapper.java index e067457e..c24cf9de 100644 --- a/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocFieldWrapper.java +++ b/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocFieldWrapper.java @@ -1,23 +1,62 @@ package org.jsondoc.core.util; -import java.lang.reflect.Field; +import java.lang.reflect.Type; + +import org.jsondoc.core.annotation.ApiObjectField; public class JSONDocFieldWrapper implements Comparable { - private Field field; - private Integer order; + private String name; + private Class type; + private Type genericType; + private ApiObjectField apiObjectFieldAnnotation; + + private Integer order; - public JSONDocFieldWrapper(Field field, Integer order) { - this.field = field; + public JSONDocFieldWrapper( + String name, + Class type, + Type genericType, + ApiObjectField apiObjectFieldAnnotation, + Integer order) { + + this.name = name; + this.type = type; + this.genericType = genericType; + this.apiObjectFieldAnnotation = apiObjectFieldAnnotation; this.order = order; } - public Field getField() { - return field; - } + public String getName() { + return name; + } - public void setField(Field field) { - this.field = field; - } + public void setName(String name) { + this.name = name; + } + + public Class getType() { + return type; + } + + public void setType(Class type) { + this.type = type; + } + + public Type getGenericType() { + return genericType; + } + + public void setGenericType(Type genericType) { + this.genericType = genericType; + } + + public ApiObjectField getApiObjectFieldAnnotation() { + return apiObjectFieldAnnotation; + } + + public void setApiObjectFieldAnnotation(ApiObjectField apiObjectFieldAnnotation) { + this.apiObjectFieldAnnotation = apiObjectFieldAnnotation; + } public Integer getOrder() { return order; @@ -33,7 +72,7 @@ public void setOrder(Integer order) { @Override public int compareTo(JSONDocFieldWrapper o) { if(this.getOrder().equals(o.getOrder())) { - return this.getField().getName().compareTo(o.getField().getName()); + return this.getName().compareTo(o.getName()); } else { return this.getOrder() - o.getOrder(); } diff --git a/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocHibernateValidatorProcessor.java b/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocHibernateValidatorProcessor.java index 4ce67ce0..0138e76f 100644 --- a/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocHibernateValidatorProcessor.java +++ b/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocHibernateValidatorProcessor.java @@ -1,6 +1,6 @@ package org.jsondoc.core.util; -import java.lang.reflect.Field; +import java.lang.reflect.AnnotatedElement; import javax.validation.constraints.AssertFalse; import javax.validation.constraints.AssertTrue; @@ -49,7 +49,7 @@ public class JSONDocHibernateValidatorProcessor { private final static String CreditCardNumber_message = "must be a valid credit card number"; private final static String ScriptAssert_message = "script expression %s didn't evaluate to true"; - public static void processHibernateValidatorAnnotations(Field field, ApiObjectFieldDoc apiPojoFieldDoc) { + public static void processHibernateValidatorAnnotations(AnnotatedElement field, ApiObjectFieldDoc apiPojoFieldDoc) { try { Class.forName("org.hibernate.validator.constraints.NotBlank"); diff --git a/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocTemplateBuilder.java b/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocTemplateBuilder.java index 1e89263d..f131874f 100644 --- a/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocTemplateBuilder.java +++ b/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocTemplateBuilder.java @@ -1,6 +1,7 @@ package org.jsondoc.core.util; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; @@ -40,10 +41,9 @@ public static JSONDocTemplate build(Class clazz, Set> jsondocObjects try { Set fields = getAllDeclaredFields(clazz); - for (JSONDocFieldWrapper jsondocFieldWrapper : fields) { - Field field = jsondocFieldWrapper.getField(); - String fieldName = field.getName(); - ApiObjectField apiObjectField = field.getAnnotation(ApiObjectField.class); + for (JSONDocFieldWrapper wrapper : fields) { + String fieldName = wrapper.getName(); + ApiObjectField apiObjectField = wrapper.getApiObjectFieldAnnotation(); if (apiObjectField != null && !apiObjectField.name().isEmpty()) { fieldName = apiObjectField.name(); } @@ -51,10 +51,10 @@ public static JSONDocTemplate build(Class clazz, Set> jsondocObjects Object value; // This condition is to avoid StackOverflow in case class "A" // contains a field of type "A" - if (field.getType().equals(clazz) || (apiObjectField != null && !apiObjectField.processtemplate())) { - value = getValue(Object.class, field.getGenericType(), fieldName, jsondocObjects); + if (wrapper.getType().equals(clazz) || (apiObjectField != null && !apiObjectField.processtemplate())) { + value = getValue(Object.class, wrapper.getGenericType(), fieldName, jsondocObjects); } else { - value = getValue(field.getType(), field.getGenericType(), fieldName, jsondocObjects); + value = getValue(wrapper.getType(), wrapper.getGenericType(), fieldName, jsondocObjects); } jsonDocTemplate.put(fieldName, value); @@ -107,20 +107,38 @@ private static Set getAllDeclaredFields(Class clazz) { for (Field field : declaredFields) { if (field.isAnnotationPresent(ApiObjectField.class)) { ApiObjectField annotation = field.getAnnotation(ApiObjectField.class); - fields.add(new JSONDocFieldWrapper(field, annotation.order())); + fields.add(new JSONDocFieldWrapper(field.getName(), field.getType(), field.getGenericType(), annotation, annotation.order())); } else { - fields.add(new JSONDocFieldWrapper(field, Integer.MAX_VALUE)); + fields.add(new JSONDocFieldWrapper(field.getName(), field.getType(), field.getGenericType(), null, Integer.MAX_VALUE)); } } - if (clazz.getSuperclass() != null) { + List declaredMethods = new ArrayList(); + if (clazz.isEnum()) { + return fields; + } else { + declaredMethods.addAll(Arrays.asList(clazz.getDeclaredMethods())); + } + + for (Method method : declaredMethods) { + if (JSONDocUtils.isFieldMethod(method)) { + if (method.isAnnotationPresent(ApiObjectField.class)) { + ApiObjectField annotation = method.getAnnotation(ApiObjectField.class); + fields.add(new JSONDocFieldWrapper(JSONDocUtils.getPropertyName(method), method.getReturnType(), method.getGenericReturnType(), annotation, annotation.order())); + } else { + fields.add(new JSONDocFieldWrapper(JSONDocUtils.getPropertyName(method), method.getReturnType(), method.getGenericReturnType(), null, Integer.MAX_VALUE)); + } + } + } + + if (clazz.getSuperclass() != null && !clazz.getSuperclass().isAssignableFrom(Object.class)) { fields.addAll(getAllDeclaredFields(clazz.getSuperclass())); } - - return fields; + + return fields; } - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") private static Class wrap(Class clazz) { return clazz.isPrimitive() ? (Class) primitives.get(clazz) : clazz; } diff --git a/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocUtils.java b/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocUtils.java index 6fe672d2..2200cb19 100644 --- a/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocUtils.java +++ b/jsondoc-core/src/main/java/org/jsondoc/core/util/JSONDocUtils.java @@ -3,6 +3,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import org.jsondoc.core.annotation.ApiObjectField; + public class JSONDocUtils { public static Integer getIndexOfParameterWithAnnotation(Method method, Class a) { @@ -16,5 +18,17 @@ public static Integer getIndexOfParameterWithAnnotation(Method method, Class } return -1; } + + public static boolean isFieldMethod(Method method) { + return (method.getAnnotation(ApiObjectField.class) != null); + } + public static String getPropertyName(Method method) { + String name = method.getName(); + if ((name.startsWith("get") || name.startsWith("set")) && name.length() > 3) { + return name.substring(3, 4).toLowerCase() + name.substring(4); + } else { + return name; + } + } } diff --git a/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/AbstractSpringJSONDocScanner.java b/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/AbstractSpringJSONDocScanner.java index 8ae6fec1..6e9bc8b5 100644 --- a/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/AbstractSpringJSONDocScanner.java +++ b/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/AbstractSpringJSONDocScanner.java @@ -155,6 +155,11 @@ public Set> jsondocObjects(List packages) { for (Field field : clazz.getDeclaredFields()) { subCandidates.addAll(buildJSONDocObjectsCandidates(subCandidates, field.getType(), field.getGenericType())); } + for (Method method : clazz.getDeclaredMethods()) { + if (JSONDocUtils.isFieldMethod(method)) { + subCandidates.addAll(buildJSONDocObjectsCandidates(subCandidates, method.getReturnType(), method.getGenericReturnType())); + } + } } candidates.addAll(subCandidates); diff --git a/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/builder/SpringObjectBuilder.java b/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/builder/SpringObjectBuilder.java index 048affed..0974873f 100644 --- a/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/builder/SpringObjectBuilder.java +++ b/jsondoc-springmvc/src/main/java/org/jsondoc/springmvc/scanner/builder/SpringObjectBuilder.java @@ -1,6 +1,7 @@ package org.jsondoc.springmvc.scanner.builder; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Set; import java.util.TreeSet; @@ -11,6 +12,7 @@ import org.jsondoc.core.util.JSONDocHibernateValidatorProcessor; import org.jsondoc.core.util.JSONDocType; import org.jsondoc.core.util.JSONDocTypeBuilder; +import org.jsondoc.core.util.JSONDocUtils; public class SpringObjectBuilder { @@ -32,8 +34,22 @@ public static ApiObjectDoc buildObject(Class clazz) { fieldDocs.add(fieldDoc); } - Class superclass = clazz.getSuperclass(); - if (superclass != null) { + for (Method method : clazz.getDeclaredMethods()) { + if (JSONDocUtils.isFieldMethod(method)) { + ApiObjectFieldDoc fieldDoc = new ApiObjectFieldDoc(); + fieldDoc.setName(JSONDocUtils.getPropertyName(method)); + fieldDoc.setOrder(Integer.MAX_VALUE); + fieldDoc.setRequired(DefaultJSONDocScanner.UNDEFINED.toUpperCase()); + fieldDoc.setJsondocType(JSONDocTypeBuilder.build(new JSONDocType(), method.getReturnType(), method.getGenericReturnType())); + + JSONDocHibernateValidatorProcessor.processHibernateValidatorAnnotations(method, fieldDoc); + + fieldDocs.add(fieldDoc); + } + } + + Class superclass = clazz.getSuperclass(); + if (superclass != null && !superclass.isAssignableFrom(Object.class) && !superclass.isEnum()) { ApiObjectDoc parentObjectDoc = buildObject(superclass); fieldDocs.addAll(parentObjectDoc.getFields()); } diff --git a/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/method/ApiObjectFieldAnnotationOnMethodTest.java b/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/method/ApiObjectFieldAnnotationOnMethodTest.java new file mode 100644 index 00000000..e30c43e3 --- /dev/null +++ b/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/method/ApiObjectFieldAnnotationOnMethodTest.java @@ -0,0 +1,89 @@ +package org.jsondoc.spring4mvc.scanner.method; + + +import static org.springframework.web.bind.annotation.RequestMethod.GET; + +import java.util.Set; + +import org.jsondoc.core.annotation.ApiObject; +import org.jsondoc.core.annotation.ApiObjectField; +import org.jsondoc.core.pojo.ApiObjectDoc; +import org.jsondoc.core.pojo.ApiObjectFieldDoc; +import org.jsondoc.core.pojo.JSONDoc; +import org.jsondoc.core.pojo.JSONDoc.MethodDisplay; +import org.jsondoc.springmvc.scanner.Spring4JSONDocScanner; +import org.junit.Test; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.google.common.collect.Lists; + +public class ApiObjectFieldAnnotationOnMethodTest { + + @Test + public void testSpring4JSONDocScanner() { + Spring4JSONDocScanner jsondocScanner = new Spring4JSONDocScanner(); + JSONDoc jsonDoc = jsondocScanner.getJSONDoc("1.0", "/field", Lists.newArrayList("org.jsondoc.spring4mvc.scanner"), true, MethodDisplay.URI); + for (Set o : jsonDoc.getObjects().values()) { + for (ApiObjectDoc api : o) { + System.out.println(api.getName()); + for (ApiObjectFieldDoc field : api.getFields()) { + System.out.println(" " + field.getName()); + } + } + } + } + + @RestController + @RequestMapping(value="/spring4") + public class Spring4RestController { + + @RequestMapping(value="/method", method=GET) + public Spring4ObjectMethod getMethod() { + return new Spring4ObjectMethod() { + @Override + public String getName() { return null; } + @Override + public Long getLocation() { return null; } + @Override + public Long getA() { return null; } + @Override + public Long b() { return null; } + @Override + public Boolean setRetired() { return null; } + }; + } + } + + @ApiObject(name="Spring4ObjectMethod", show=true) + public static abstract class Spring4ObjectMethod { + + private String adress; + + @ApiObjectField + public abstract String getName(); + + @ApiObjectField + public abstract Boolean setRetired(); + + // Missing @ApiObjectField + public abstract Long getLocation(); + + @ApiObjectField + public abstract Long getA(); + + @ApiObjectField + public abstract Long b(); + + public String getAdress() { + return adress; + } + + public void setAdress(String adress) { + this.adress = adress; + } + + } +} + + diff --git a/spring-boot-starter-jsondoc/.gitignore b/spring-boot-starter-jsondoc/.gitignore index b83d2226..4972e7d7 100644 --- a/spring-boot-starter-jsondoc/.gitignore +++ b/spring-boot-starter-jsondoc/.gitignore @@ -1 +1,2 @@ /target/ +/.apt_generated/ From e62b5c892a67c4ac48d3a80be0568e025abad3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Frantz=C3=A9n?= Date: Mon, 26 Oct 2015 16:10:26 +0100 Subject: [PATCH 3/3] Issue 163 Make it possible to add @ApiObjectField annotation on getters and setters Improved unit tests --- .../ApiObjectFieldAnnotationOnMethodTest.java | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/method/ApiObjectFieldAnnotationOnMethodTest.java b/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/method/ApiObjectFieldAnnotationOnMethodTest.java index e30c43e3..15b0aa24 100644 --- a/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/method/ApiObjectFieldAnnotationOnMethodTest.java +++ b/jsondoc-springmvc/src/test/java/org/jsondoc/spring4mvc/scanner/method/ApiObjectFieldAnnotationOnMethodTest.java @@ -3,6 +3,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.GET; +import java.util.Arrays; import java.util.Set; import org.jsondoc.core.annotation.ApiObject; @@ -12,6 +13,7 @@ import org.jsondoc.core.pojo.JSONDoc; import org.jsondoc.core.pojo.JSONDoc.MethodDisplay; import org.jsondoc.springmvc.scanner.Spring4JSONDocScanner; +import org.junit.Assert; import org.junit.Test; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -23,15 +25,19 @@ public class ApiObjectFieldAnnotationOnMethodTest { @Test public void testSpring4JSONDocScanner() { Spring4JSONDocScanner jsondocScanner = new Spring4JSONDocScanner(); - JSONDoc jsonDoc = jsondocScanner.getJSONDoc("1.0", "/field", Lists.newArrayList("org.jsondoc.spring4mvc.scanner"), true, MethodDisplay.URI); - for (Set o : jsonDoc.getObjects().values()) { - for (ApiObjectDoc api : o) { - System.out.println(api.getName()); - for (ApiObjectFieldDoc field : api.getFields()) { - System.out.println(" " + field.getName()); - } - } - } + JSONDoc jsonDoc = jsondocScanner.getJSONDoc("1.0", "/field", Lists.newArrayList("org.jsondoc.spring4mvc.scanner.method"), true, MethodDisplay.URI); + Assert.assertEquals(1, jsonDoc.getObjects().size()); + Set objects = jsonDoc.getObjects().get(""); + ApiObjectDoc o = objects.iterator().next(); + ApiObjectFieldDoc[] fields = o.getFields().toArray(new ApiObjectFieldDoc[]{}); + Arrays.sort(fields); + Assert.assertEquals(6, fields.length); + Assert.assertEquals("a", fields[0].getName()); + Assert.assertEquals("address", fields[1].getName()); + Assert.assertEquals("b", fields[2].getName()); + Assert.assertEquals("get", fields[3].getName()); + Assert.assertEquals("name", fields[4].getName()); + Assert.assertEquals("retired", fields[5].getName()); } @RestController @@ -51,6 +57,8 @@ public Spring4ObjectMethod getMethod() { public Long b() { return null; } @Override public Boolean setRetired() { return null; } + @Override + public Boolean get() { return null; } }; } } @@ -58,7 +66,7 @@ public Spring4ObjectMethod getMethod() { @ApiObject(name="Spring4ObjectMethod", show=true) public static abstract class Spring4ObjectMethod { - private String adress; + private String address; @ApiObjectField public abstract String getName(); @@ -66,6 +74,9 @@ public static abstract class Spring4ObjectMethod { @ApiObjectField public abstract Boolean setRetired(); + @ApiObjectField + public abstract Boolean get(); + // Missing @ApiObjectField public abstract Long getLocation(); @@ -75,14 +86,13 @@ public static abstract class Spring4ObjectMethod { @ApiObjectField public abstract Long b(); - public String getAdress() { - return adress; + public String getAddress() { + return address; } - public void setAdress(String adress) { - this.adress = adress; + public void setAddress(String address) { + this.address = address; } - } }