From 311912c70d6b3303ed5479687be200248a33d925 Mon Sep 17 00:00:00 2001 From: Hans Barnard Date: Fri, 27 Oct 2017 14:48:07 +0100 Subject: [PATCH 1/2] Fix incorrect static mocking. The original JobRegistry should be saved only once. --- .../SubResourceLocatorMethodContentAnalyzerTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/bytecode/SubResourceLocatorMethodContentAnalyzerTest.java b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/bytecode/SubResourceLocatorMethodContentAnalyzerTest.java index 7a34af7..0da0c7b 100644 --- a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/bytecode/SubResourceLocatorMethodContentAnalyzerTest.java +++ b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/bytecode/SubResourceLocatorMethodContentAnalyzerTest.java @@ -8,6 +8,7 @@ import com.sebastian_daschner.jaxrs_analyzer.model.results.ClassResult; import com.sebastian_daschner.jaxrs_analyzer.model.results.MethodResult; import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -97,6 +98,11 @@ public void test() throws IOException { assertEquals("failed for " + testClassName, expectedClassNames, captor.getAllValues().stream().collect(Collectors.toSet())); verify(jobRegistry, times(expectedClassNames.size())).analyzeResourceClass(any(), any()); } + + @BeforeClass + public static void saveJobRegistryBeforeAllTests() { + originalJobRegistry = JobRegistry.getInstance(); + } @AfterClass public static void tearDown() throws NoSuchFieldException, IllegalAccessException { @@ -106,7 +112,6 @@ public static void tearDown() throws NoSuchFieldException, IllegalAccessExceptio private static void injectJobRegistry(final JobRegistry jobRegistry) throws NoSuchFieldException, IllegalAccessException { final Field field = JobRegistry.class.getDeclaredField("INSTANCE"); field.setAccessible(true); - originalJobRegistry = JobRegistry.getInstance(); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); From f3b606fce52f743b5a3cfc7946ed47785d0a9121 Mon Sep 17 00:00:00 2001 From: Hans Barnard Date: Fri, 27 Oct 2017 14:59:36 +0100 Subject: [PATCH 2/2] Add support for adhering to Jackson annotations instead of JAXB with JAXB the default. --- .gitignore | 3 + Documentation.adoc | 1 + README.adoc | 5 + pom.xml | 7 +- .../jaxrs_analyzer/JAXRSAnalyzer.java | 8 +- .../jaxrs_analyzer/Main.java | 31 ++- .../analysis/ProjectAnalyzer.java | 5 +- .../analysis/results/JacksonAnalyzer.java | 84 ++++++++ .../results/JacksonAnalyzerFactory.java | 10 + .../analysis/results/JavaTypeAnalyzer.java | 178 ++--------------- .../analysis/results/JaxbAnalyzer.java | 188 ++++++++++++++++++ .../analysis/results/JaxbAnalyzerFactory.java | 10 + .../results/NormalizedTypeAnalyzer.java | 11 + .../NormalizedTypeAnalyzerFactory.java | 7 + .../analysis/results/ResultInterpreter.java | 4 +- .../analysis/ProjectAnalyzerTest.java | 3 +- .../results/JavaTypeAnalyzerTest.java | 3 +- .../results/ResultInterpreterTest.java | 24 ++- .../typeanalyzer/TestClassJackson1.java | 67 +++++++ .../typeanalyzer/TestClassJackson2.java | 96 +++++++++ .../typeanalyzer/TestClassJackson3.java | 65 ++++++ 21 files changed, 626 insertions(+), 184 deletions(-) create mode 100644 src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JacksonAnalyzer.java create mode 100644 src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JacksonAnalyzerFactory.java create mode 100644 src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JaxbAnalyzer.java create mode 100644 src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JaxbAnalyzerFactory.java create mode 100644 src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/NormalizedTypeAnalyzer.java create mode 100644 src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/NormalizedTypeAnalyzerFactory.java create mode 100644 src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson1.java create mode 100644 src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson2.java create mode 100644 src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson3.java diff --git a/.gitignore b/.gitignore index 6d7a600..d007033 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ dependency-reduced-pom.xml .idea/ *.class +/.classpath +/.project +/.settings diff --git a/Documentation.adoc b/Documentation.adoc index b1e0856..52f59dc 100644 --- a/Documentation.adoc +++ b/Documentation.adoc @@ -51,6 +51,7 @@ Following available options: * `-v ` The version of the project * `-d ` The domain of the project * `-o ` The location of the analysis output (will be printed to standard out if omitted) +* `-ta ` Annotations to use for type analyzer: jaxb (default) or jackson Following available backend specific options (only have effect if the corresponding backend is selected): diff --git a/README.adoc b/README.adoc index 8c19a36..36bc724 100644 --- a/README.adoc +++ b/README.adoc @@ -25,6 +25,11 @@ You can download the latest version https://github.com/sdaschner/jaxrs-analyzer/ == Backends The Analyzer supports Plaintext, AsciiDoc and Swagger as output format. +== Annotations for Type Analyzer +By default JAXB annotations and visibility rules will determine which fields will be serialized to JSON. +Alternatively Jackson annotations can be used which will then determine visibility as well as adhere to property names +as specified by the @JsonProperty("my_property_name") annotation. + == Documentation / Feature list +...+ can be found https://github.com/sdaschner/jaxrs-analyzer/blob/master/Documentation.adoc[here]. diff --git a/pom.xml b/pom.xml index db966bc..c021df9 100644 --- a/pom.xml +++ b/pom.xml @@ -42,10 +42,15 @@ asm-all 5.1 + + com.fasterxml.jackson.core + jackson-databind + 2.9.2 + com.fasterxml.jackson.core jackson-annotations - 2.8.5 + 2.9.2 diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/JAXRSAnalyzer.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/JAXRSAnalyzer.java index 26e383b..a9a1faa 100644 --- a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/JAXRSAnalyzer.java +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/JAXRSAnalyzer.java @@ -1,6 +1,7 @@ package com.sebastian_daschner.jaxrs_analyzer; import com.sebastian_daschner.jaxrs_analyzer.analysis.ProjectAnalyzer; +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.NormalizedTypeAnalyzerFactory; import com.sebastian_daschner.jaxrs_analyzer.backend.Backend; import com.sebastian_daschner.jaxrs_analyzer.model.rest.Project; import com.sebastian_daschner.jaxrs_analyzer.model.rest.Resources; @@ -29,6 +30,7 @@ public class JAXRSAnalyzer { private final String projectVersion; private final Path outputLocation; private final Backend backend; + private final NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory; /** * Constructs a JAX-RS Analyzer. @@ -42,13 +44,14 @@ public class JAXRSAnalyzer { * @param outputLocation The location of the output file (output will be printed to standard out if {@code null}) */ public JAXRSAnalyzer(final Set projectClassPaths, final Set projectSourcePaths, final Set classPaths, final String projectName, final String projectVersion, - final Backend backend, final Path outputLocation) { + final Backend backend, final Path outputLocation, final NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory) { Objects.requireNonNull(projectClassPaths); Objects.requireNonNull(projectSourcePaths); Objects.requireNonNull(classPaths); Objects.requireNonNull(projectName); Objects.requireNonNull(projectVersion); Objects.requireNonNull(backend); + Objects.requireNonNull(normalizedTypeAnalyzerFactory); if (projectClassPaths.isEmpty()) throw new IllegalArgumentException("At least one project path is mandatory"); @@ -60,13 +63,14 @@ public JAXRSAnalyzer(final Set projectClassPaths, final Set projectS this.projectVersion = projectVersion; this.outputLocation = outputLocation; this.backend = backend; + this.normalizedTypeAnalyzerFactory = normalizedTypeAnalyzerFactory; } /** * Analyzes the JAX-RS project at the class path and produces the output as configured. */ public void analyze() { - final Resources resources = new ProjectAnalyzer(classPaths).analyze(projectClassPaths, projectSourcePaths); + final Resources resources = new ProjectAnalyzer(classPaths).analyze(projectClassPaths, projectSourcePaths,normalizedTypeAnalyzerFactory); if (resources.isEmpty()) { LogProvider.info("Empty JAX-RS analysis result, omitting output"); diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/Main.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/Main.java index fed97ad..40c249c 100644 --- a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/Main.java +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/Main.java @@ -15,6 +15,9 @@ */ package com.sebastian_daschner.jaxrs_analyzer; +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.JacksonAnalyzerFactory; +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.JaxbAnalyzerFactory; +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.NormalizedTypeAnalyzerFactory; import com.sebastian_daschner.jaxrs_analyzer.backend.Backend; import com.sebastian_daschner.jaxrs_analyzer.backend.StringBackend; import com.sebastian_daschner.jaxrs_analyzer.backend.swagger.SwaggerOptions; @@ -44,6 +47,7 @@ public class Main { private static String version = DEFAULT_VERSION; private static String backendType = "swagger"; private static Path outputFileLocation; + private static String typeAnalyzer = "jaxb"; /** * Inspects JAX-RS projects and outputs the gathered information. @@ -90,8 +94,10 @@ public static void main(final String... args) { final Backend backend = JAXRSAnalyzer.constructBackend(backendType); backend.configure(attributes); - - final JAXRSAnalyzer jaxrsAnalyzer = new JAXRSAnalyzer(projectClassPaths, projectSourcePaths, classPaths, name, version, backend, outputFileLocation); + + NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory = createTypeAnalyzerFactory(); + final JAXRSAnalyzer jaxrsAnalyzer = new JAXRSAnalyzer(projectClassPaths, projectSourcePaths, classPaths, name, version, backend + , outputFileLocation,normalizedTypeAnalyzerFactory); jaxrsAnalyzer.analyze(); } @@ -127,6 +133,9 @@ private static void extractArgs(String[] args) { case "-e": System.setProperty("project.build.sourceEncoding", args[++i]); break; + case "-ta": + typeAnalyzer = args[++i]; + break; case "--swaggerSchemes": attributes.put(SwaggerOptions.SWAGGER_SCHEMES, args[++i]); break; @@ -208,6 +217,7 @@ private static void printUsageAndExit() { System.err.println(" -o The location of the analysis output (will be printed to standard out if omitted)"); System.err.println(" -a = Set custom attributes for backends."); System.err.println(" -e The source file encoding"); + System.err.println(" -ta Annotations to use for type analyzer: jaxb (default) or jackson"); System.err.println("\nFollowing available backend specific options (only have effect if the corresponding backend is selected):\n"); System.err.println(" --swaggerSchemes [,schemes] The Swagger schemes: http (default), https, ws, wss"); System.err.println(" --renderSwaggerTags Enables rendering of Swagger tags (default tag will be used per default)"); @@ -217,4 +227,21 @@ private static void printUsageAndExit() { System.exit(1); } + private static NormalizedTypeAnalyzerFactory createTypeAnalyzerFactory() { + NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory = null; + switch (typeAnalyzer) { + case "jaxb": + normalizedTypeAnalyzerFactory = new JaxbAnalyzerFactory(); + break; + case "jackson": + normalizedTypeAnalyzerFactory = new JacksonAnalyzerFactory(); + break; + default: + System.err.println("Invalid type analyzer: "+typeAnalyzer); + printUsageAndExit(); + } + return normalizedTypeAnalyzerFactory; + } + + } diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/ProjectAnalyzer.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/ProjectAnalyzer.java index 4523172..252fad1 100644 --- a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/ProjectAnalyzer.java +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/ProjectAnalyzer.java @@ -21,6 +21,7 @@ import com.sebastian_daschner.jaxrs_analyzer.analysis.classes.ContextClassReader; import com.sebastian_daschner.jaxrs_analyzer.analysis.classes.JAXRSClassVisitor; import com.sebastian_daschner.jaxrs_analyzer.analysis.javadoc.JavaDocAnalyzer; +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.NormalizedTypeAnalyzerFactory; import com.sebastian_daschner.jaxrs_analyzer.analysis.results.ResultInterpreter; import com.sebastian_daschner.jaxrs_analyzer.model.rest.Resources; import com.sebastian_daschner.jaxrs_analyzer.model.results.ClassResult; @@ -86,7 +87,7 @@ public ProjectAnalyzer(final Set classPaths) { * @param projectSourcePaths The project source file paths * @return The REST resource representations */ - public Resources analyze(final Set projectClassPaths, final Set projectSourcePaths) { + public Resources analyze(final Set projectClassPaths, final Set projectSourcePaths, NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory) { lock.lock(); try { projectClassPaths.forEach(this::addProjectPath); @@ -109,7 +110,7 @@ public Resources analyze(final Set projectClassPaths, final Set proj javaDocAnalyzer.analyze(classResults, packages, projectSourcePaths, classPool); - return resultInterpreter.interpret(classResults); + return resultInterpreter.interpret(classResults,normalizedTypeAnalyzerFactory); } finally { lock.unlock(); } diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JacksonAnalyzer.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JacksonAnalyzer.java new file mode 100644 index 0000000..41296f6 --- /dev/null +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JacksonAnalyzer.java @@ -0,0 +1,84 @@ +package com.sebastian_daschner.jaxrs_analyzer.analysis.results; + +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getFieldDescriptor; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getMethodSignature; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getReturnType; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier; +import com.sebastian_daschner.jaxrs_analyzer.utils.Pair; + +public class JacksonAnalyzer implements NormalizedTypeAnalyzer { + + private final JavaTypeAnalyzer javaTypeAnalyzer; + private final ObjectMapper objectMapper = new ObjectMapper(); + + public JacksonAnalyzer(JavaTypeAnalyzer javaTypeAnalyzer) { + this.javaTypeAnalyzer = javaTypeAnalyzer; + } + + @Override + public Map analyzeClass(String type, Class clazz) { + if (clazz == null || JavaTypeAnalyzer.isJDKType(type)) + return Collections.emptyMap(); + + JavaType jacksonType = objectMapper.getTypeFactory().constructType(clazz); + BeanDescription introspection = objectMapper.getSerializationConfig().introspect(jacksonType); + List jacksonProperties = introspection.findProperties(); + final Map properties = new HashMap<>(); + jacksonProperties + .stream() + .filter(bp->bp.couldSerialize()) + .map(bp->mapProperty(bp, type)) + .filter(Objects::nonNull) + .forEach(p->{ + properties.put(p.getLeft(), TypeIdentifier.ofType(p.getRight())); + javaTypeAnalyzer.analyze(p.getRight()); + }); + ; + return properties; + } + + private Pair mapProperty(BeanPropertyDefinition property, final String containedType) { + AnnotatedMember primaryMember = property.getPrimaryMember(); + if (primaryMember instanceof AnnotatedField) { + AnnotatedField annotatedField = (AnnotatedField)primaryMember; + return mapField(property.getName(),annotatedField.getAnnotated(),containedType); + } else if (primaryMember instanceof AnnotatedMethod) { + AnnotatedMethod annotatedMethod = (AnnotatedMethod)primaryMember; + return mapGetter(property.getName(),annotatedMethod.getAnnotated(),containedType); + } + return null; + } + + private static Pair mapField(final String jsonProperty, final Field field, final String containedType) { + final String type = getFieldDescriptor(field, containedType); + if (type == null) + return null; + + return Pair.of(jsonProperty, type); + } + + private static Pair mapGetter(final String jsonProperty,final Method method, final String containedType) { + final String returnType = getReturnType(getMethodSignature(method), containedType); + if (returnType == null) + return null; + return Pair.of(jsonProperty, returnType); + } + + +} diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JacksonAnalyzerFactory.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JacksonAnalyzerFactory.java new file mode 100644 index 0000000..b54ac53 --- /dev/null +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JacksonAnalyzerFactory.java @@ -0,0 +1,10 @@ +package com.sebastian_daschner.jaxrs_analyzer.analysis.results; + +public class JacksonAnalyzerFactory implements NormalizedTypeAnalyzerFactory { + + @Override + public NormalizedTypeAnalyzer create(JavaTypeAnalyzer javaTypeAnalyzer) { + return new JacksonAnalyzer(javaTypeAnalyzer); + } + +} diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JavaTypeAnalyzer.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JavaTypeAnalyzer.java index e9fd9a6..390f6be 100644 --- a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JavaTypeAnalyzer.java +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JavaTypeAnalyzer.java @@ -16,25 +16,18 @@ package com.sebastian_daschner.jaxrs_analyzer.analysis.results; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreType; -import com.sebastian_daschner.jaxrs_analyzer.model.Types; -import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier; -import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeRepresentation; -import com.sebastian_daschner.jaxrs_analyzer.utils.Pair; -import org.objectweb.asm.Type; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.isAssignableTo; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.loadClassFromType; +import static com.sebastian_daschner.jaxrs_analyzer.model.Types.COLLECTION; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlTransient; -import java.lang.reflect.*; -import java.util.*; -import java.util.stream.Collectors; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.stream.Stream; -import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.*; -import static com.sebastian_daschner.jaxrs_analyzer.model.Types.COLLECTION; +import com.sebastian_daschner.jaxrs_analyzer.model.Types; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeRepresentation; /** * Analyzes a class (usually a POJO) for it's properties and methods. @@ -44,17 +37,16 @@ */ class JavaTypeAnalyzer { - private final static String[] NAMES_TO_IGNORE = {"getClass"}; - private static Set ignoredFieldNames = new HashSet<>(); - /** * The type representation storage where all analyzed types have to be added. This will be created by the caller. */ private final Map typeRepresentations; private final Set analyzedTypes; - - JavaTypeAnalyzer(final Map typeRepresentations) { + private final NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory; + + JavaTypeAnalyzer(final Map typeRepresentations, NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory) { this.typeRepresentations = typeRepresentations; + this.normalizedTypeAnalyzerFactory = normalizedTypeAnalyzerFactory; analyzedTypes = new HashSet<>(); } @@ -78,11 +70,12 @@ TypeIdentifier analyze(final String rootType) { return identifier; } - private static boolean isJDKType(final String type) { + static boolean isJDKType(final String type) { // exclude java, javax, etc. packages return Types.PRIMITIVE_TYPES.contains(type) || type.startsWith("Ljava/") || type.startsWith("Ljavax/"); } + private TypeRepresentation analyzeInternal(final TypeIdentifier identifier, final String type) { if (isAssignableTo(type, COLLECTION)) { final String containedType = ResponseTypeNormalizer.normalizeCollection(type); @@ -92,147 +85,8 @@ private TypeRepresentation analyzeInternal(final TypeIdentifier identifier, fina final Class loadedClass = loadClassFromType(type); if (loadedClass != null && loadedClass.isEnum()) return TypeRepresentation.ofEnum(identifier, Stream.of(loadedClass.getEnumConstants()).map(o -> (Enum) o).map(Enum::name).toArray(String[]::new)); - - return TypeRepresentation.ofConcrete(identifier, analyzeClass(type, loadedClass)); - } - - private Map analyzeClass(final String type, final Class clazz) { - if (clazz == null || isJDKType(type)) - return Collections.emptyMap(); - - final XmlAccessType value = getXmlAccessType(clazz); - - // TODO analyze & test annotation inheritance - ignoredFieldNames.clear(); - final List relevantFields = Stream.of(clazz.getDeclaredFields()).filter(f -> isRelevant(f, value)).collect(Collectors.toList()); - final List relevantGetters = Stream.of(clazz.getDeclaredMethods()).filter(m -> isRelevant(m, value)).collect(Collectors.toList()); - - final Map properties = new HashMap<>(); - - final Stream> allSuperTypes = Stream.concat(Stream.of(clazz.getInterfaces()), Stream.of(clazz.getSuperclass())); - allSuperTypes.filter(Objects::nonNull).map(Type::getDescriptor).map(t -> analyzeClass(t, loadClassFromType(t))).forEach(properties::putAll); - - Stream.concat(relevantFields.stream().map(f -> mapField(f, type)), relevantGetters.stream().map(g -> mapGetter(g, type))) - .filter(Objects::nonNull).forEach(p -> { - properties.put(p.getLeft(), TypeIdentifier.ofType(p.getRight())); - analyze(p.getRight()); - }); - - return properties; - } - - private XmlAccessType getXmlAccessType(final Class clazz) { - Class current = clazz; - - while (current != null) { - if (isAnnotationPresent(current, XmlAccessorType.class)) - return getAnnotation(current, XmlAccessorType.class).value(); - current = current.getSuperclass(); - } - - return XmlAccessType.PUBLIC_MEMBER; - } - - private static boolean isRelevant(final Field field, final XmlAccessType accessType) { - if (field.isSynthetic()) - return false; - - if (hasIgnoreAnnotation(field) || isTypeIgnored(field.getType())) { - ignoredFieldNames.add(field.getName()); - return false; - } - - if (isAnnotationPresent(field, XmlElement.class)) - return true; - - final int modifiers = field.getModifiers(); - if (accessType == XmlAccessType.FIELD) - // always take, unless static or transient - return !Modifier.isTransient(modifiers) && !Modifier.isStatic(modifiers) && !isAnnotationPresent(field, XmlTransient.class); - else if (accessType == XmlAccessType.PUBLIC_MEMBER) - // only for public, non-static - return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && !isAnnotationPresent(field, XmlTransient.class); - - return false; - } - - private static boolean hasIgnoreAnnotation(final T member) { - return isAnnotationPresent(member, JsonIgnore.class) || isTypeIgnored(member.getDeclaringClass()); - } - - private static boolean isTypeIgnored(final Class declaringClass) { - return isAnnotationPresent(declaringClass, JsonIgnoreType.class); - } - - /** - * Checks if the method is public and non-static and that the method is a Getter. - * Does not allow methods with ignored names. - * Does also not take methods annotated with {@link XmlTransient}. - * - * @param method The method - * @return {@code true} if the method should be analyzed further - */ - private static boolean isRelevant(final Method method, final XmlAccessType accessType) { - if (method.isSynthetic() || !isGetter(method)) - return false; - - final boolean propertyIgnored = ignoredFieldNames.contains(extractPropertyName(method.getName())); - if (propertyIgnored || hasIgnoreAnnotation(method) || isTypeIgnored(method.getReturnType())) { - return false; - } - - if (isAnnotationPresent(method, XmlElement.class)) - return true; - - if (accessType == XmlAccessType.PROPERTY) - return !isAnnotationPresent(method, XmlTransient.class); - else if (accessType == XmlAccessType.PUBLIC_MEMBER) - return Modifier.isPublic(method.getModifiers()) && !isAnnotationPresent(method, XmlTransient.class); - - return false; + return TypeRepresentation.ofConcrete(identifier, normalizedTypeAnalyzerFactory.create(this).analyzeClass(type, loadedClass)); } - /** - * Converts a getter name to the property name (without the "get" or "is" and lowercase). - * - * @param name The name of the method (MUST match "get[A-Z][A-Za-z]*|is[A-Z][A-Za-z]*") - * @return The name of the property - */ - private static String extractPropertyName(final String name) { - final int size = name.startsWith("is") ? 2 : 3; - final char chars[] = name.substring(size).toCharArray(); - chars[0] = Character.toLowerCase(chars[0]); - return new String(chars); - } - - private static boolean isGetter(final Method method) { - if (Modifier.isStatic(method.getModifiers())) - return false; - - final String name = method.getName(); - if (Stream.of(NAMES_TO_IGNORE).anyMatch(n -> n.equals(name))) - return false; - - if (name.startsWith("get") && name.length() > 3) - return method.getReturnType() != void.class; - - return name.startsWith("is") && name.length() > 2 && method.getReturnType() == boolean.class; - } - - private static Pair mapField(final Field field, final String containedType) { - final String type = getFieldDescriptor(field, containedType); - if (type == null) - return null; - - return Pair.of(field.getName(), type); - } - - private static Pair mapGetter(final Method method, final String containedType) { - final String returnType = getReturnType(getMethodSignature(method), containedType); - if (returnType == null) - return null; - - return Pair.of(extractPropertyName(method.getName()), returnType); - } } diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JaxbAnalyzer.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JaxbAnalyzer.java new file mode 100644 index 0000000..26c1b21 --- /dev/null +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JaxbAnalyzer.java @@ -0,0 +1,188 @@ +package com.sebastian_daschner.jaxrs_analyzer.analysis.results; + +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getAnnotation; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getFieldDescriptor; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getMethodSignature; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getReturnType; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.isAnnotationPresent; +import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.loadClassFromType; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlTransient; + +import org.objectweb.asm.Type; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreType; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier; +import com.sebastian_daschner.jaxrs_analyzer.utils.Pair; + +public class JaxbAnalyzer implements NormalizedTypeAnalyzer { + + private final static String[] NAMES_TO_IGNORE = {"getClass"}; + private static Set ignoredFieldNames = new HashSet<>(); + private final JavaTypeAnalyzer javaTypeAnalyzer; + + JaxbAnalyzer(JavaTypeAnalyzer javaTypeAnalyzer) { + this.javaTypeAnalyzer = javaTypeAnalyzer; + } + + @Override + public Map analyzeClass(final String type, final Class clazz) { + if (clazz == null || JavaTypeAnalyzer.isJDKType(type)) + return Collections.emptyMap(); + + final XmlAccessType value = getXmlAccessType(clazz); + + // TODO analyze & test annotation inheritance + ignoredFieldNames.clear(); + final List relevantFields = Stream.of(clazz.getDeclaredFields()).filter(f -> isRelevant(f, value)).collect(Collectors.toList()); + final List relevantGetters = Stream.of(clazz.getDeclaredMethods()).filter(m -> isRelevant(m, value)).collect(Collectors.toList()); + final Map properties = new HashMap<>(); + + final Stream> allSuperTypes = Stream.concat(Stream.of(clazz.getInterfaces()), Stream.of(clazz.getSuperclass())); + allSuperTypes.filter(Objects::nonNull).map(Type::getDescriptor).map(t -> analyzeClass(t, loadClassFromType(t))).forEach(properties::putAll); + + Stream.concat(relevantFields.stream().map(f -> mapField(f, type)), relevantGetters.stream().map(g -> mapGetter(g, type))) + .filter(Objects::nonNull).forEach(p -> { + properties.put(p.getLeft(), TypeIdentifier.ofType(p.getRight())); + javaTypeAnalyzer.analyze(p.getRight()); + }); + + return properties; + } + + private XmlAccessType getXmlAccessType(final Class clazz) { + Class current = clazz; + + while (current != null) { + if (isAnnotationPresent(current, XmlAccessorType.class)) + return getAnnotation(current, XmlAccessorType.class).value(); + current = current.getSuperclass(); + } + + return XmlAccessType.PUBLIC_MEMBER; + } + + private static boolean isRelevant(final Field field, final XmlAccessType accessType) { + if (field.isSynthetic()) + return false; + + if (hasIgnoreAnnotation(field) || isTypeIgnored(field.getType())) { + ignoredFieldNames.add(field.getName()); + return false; + } + + if (isAnnotationPresent(field, XmlElement.class)) + return true; + + final int modifiers = field.getModifiers(); + if (accessType == XmlAccessType.FIELD) + // always take, unless static or transient + return !Modifier.isTransient(modifiers) && !Modifier.isStatic(modifiers) && !isAnnotationPresent(field, XmlTransient.class); + else if (accessType == XmlAccessType.PUBLIC_MEMBER) + // only for public, non-static + return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && !isAnnotationPresent(field, XmlTransient.class); + + return false; + } + + private static boolean hasIgnoreAnnotation(final T member) { + return isAnnotationPresent(member, JsonIgnore.class) || isTypeIgnored(member.getDeclaringClass()); + } + + private static boolean isTypeIgnored(final Class declaringClass) { + return isAnnotationPresent(declaringClass, JsonIgnoreType.class); + } + + /** + * Checks if the method is public and non-static and that the method is a Getter. + * Does not allow methods with ignored names. + * Does also not take methods annotated with {@link XmlTransient}. + * + * @param method The method + * @return {@code true} if the method should be analyzed further + */ + private static boolean isRelevant(final Method method, final XmlAccessType accessType) { + if (method.isSynthetic() || !isGetter(method)) + return false; + + final boolean propertyIgnored = ignoredFieldNames.contains(extractPropertyName(method.getName())); + if (propertyIgnored || hasIgnoreAnnotation(method) || isTypeIgnored(method.getReturnType())) { + return false; + } + + if (isAnnotationPresent(method, XmlElement.class)) + return true; + + if (accessType == XmlAccessType.PROPERTY) + return !isAnnotationPresent(method, XmlTransient.class); + else if (accessType == XmlAccessType.PUBLIC_MEMBER) + return Modifier.isPublic(method.getModifiers()) && !isAnnotationPresent(method, XmlTransient.class); + + return false; + } + + /** + * Converts a getter name to the property name (without the "get" or "is" and lowercase). + * + * @param name The name of the method (MUST match "get[A-Z][A-Za-z]*|is[A-Z][A-Za-z]*") + * @return The name of the property + */ + private static String extractPropertyName(final String name) { + final int size = name.startsWith("is") ? 2 : 3; + final char chars[] = name.substring(size).toCharArray(); + chars[0] = Character.toLowerCase(chars[0]); + return new String(chars); + } + + private static boolean isGetter(final Method method) { + if (Modifier.isStatic(method.getModifiers())) + return false; + + final String name = method.getName(); + if (Stream.of(NAMES_TO_IGNORE).anyMatch(n -> n.equals(name))) + return false; + + if (name.startsWith("get") && name.length() > 3) + return method.getReturnType() != void.class; + + return name.startsWith("is") && name.length() > 2 && method.getReturnType() == boolean.class; + } + + private static Pair mapField(final Field field, final String containedType) { + final String type = getFieldDescriptor(field, containedType); + if (type == null) + return null; + + return Pair.of(field.getName(), type); + } + + private static Pair mapGetter(final Method method, final String containedType) { + final String returnType = getReturnType(getMethodSignature(method), containedType); + if (returnType == null) + return null; + return Pair.of(extractPropertyName(method.getName()), returnType); + } + +} diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JaxbAnalyzerFactory.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JaxbAnalyzerFactory.java new file mode 100644 index 0000000..65a03b5 --- /dev/null +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JaxbAnalyzerFactory.java @@ -0,0 +1,10 @@ +package com.sebastian_daschner.jaxrs_analyzer.analysis.results; + +public class JaxbAnalyzerFactory implements NormalizedTypeAnalyzerFactory { + + @Override + public NormalizedTypeAnalyzer create(JavaTypeAnalyzer javaTypeAnalyzer) { + return new JaxbAnalyzer(javaTypeAnalyzer); + } + +} diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/NormalizedTypeAnalyzer.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/NormalizedTypeAnalyzer.java new file mode 100644 index 0000000..b62d1a8 --- /dev/null +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/NormalizedTypeAnalyzer.java @@ -0,0 +1,11 @@ +package com.sebastian_daschner.jaxrs_analyzer.analysis.results; + +import java.util.Map; + +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier; + +public interface NormalizedTypeAnalyzer { + + Map analyzeClass(final String type, final Class clazz); + +} diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/NormalizedTypeAnalyzerFactory.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/NormalizedTypeAnalyzerFactory.java new file mode 100644 index 0000000..b07a5c5 --- /dev/null +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/NormalizedTypeAnalyzerFactory.java @@ -0,0 +1,7 @@ +package com.sebastian_daschner.jaxrs_analyzer.analysis.results; + +public interface NormalizedTypeAnalyzerFactory { + + NormalizedTypeAnalyzer create(JavaTypeAnalyzer javaTypeAnalyzer); + +} diff --git a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/ResultInterpreter.java b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/ResultInterpreter.java index e2caa78..33171f8 100644 --- a/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/ResultInterpreter.java +++ b/src/main/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/ResultInterpreter.java @@ -53,11 +53,11 @@ public class ResultInterpreter { * * @return All REST resources */ - public Resources interpret(final Set classResults) { + public Resources interpret(final Set classResults, NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory) { resources = new Resources(); resources.setBasePath(PathNormalizer.getApplicationPath(classResults)); - javaTypeAnalyzer = new JavaTypeAnalyzer(resources.getTypeRepresentations()); + javaTypeAnalyzer = new JavaTypeAnalyzer(resources.getTypeRepresentations(),normalizedTypeAnalyzerFactory); dynamicTypeAnalyzer = new DynamicTypeAnalyzer(resources.getTypeRepresentations()); stringParameterResolver = new StringParameterResolver(resources.getTypeRepresentations(), javaTypeAnalyzer); diff --git a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/ProjectAnalyzerTest.java b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/ProjectAnalyzerTest.java index 8d46d93..e285488 100644 --- a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/ProjectAnalyzerTest.java +++ b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/ProjectAnalyzerTest.java @@ -17,6 +17,7 @@ package com.sebastian_daschner.jaxrs_analyzer.analysis; import com.sebastian_daschner.jaxrs_analyzer.LogProvider; +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.JaxbAnalyzerFactory; import com.sebastian_daschner.jaxrs_analyzer.builder.ResourceMethodBuilder; import com.sebastian_daschner.jaxrs_analyzer.builder.ResponseBuilder; import com.sebastian_daschner.jaxrs_analyzer.model.Types; @@ -72,7 +73,7 @@ public void setUp() throws MalformedURLException { @Test public void test() { final long startTime = System.currentTimeMillis(); - final Resources actualResources = classUnderTest.analyze(singleton(path), singleton(path)); + final Resources actualResources = classUnderTest.analyze(singleton(path), singleton(path),new JaxbAnalyzerFactory()); System.out.println("Project analysis took " + (System.currentTimeMillis() - startTime) + " ms"); final Resources expectedResources = getResources(); diff --git a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JavaTypeAnalyzerTest.java b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JavaTypeAnalyzerTest.java index d0c5317..43b8671 100644 --- a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JavaTypeAnalyzerTest.java +++ b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/JavaTypeAnalyzerTest.java @@ -48,7 +48,8 @@ public JavaTypeAnalyzerTest(final String testClassSimpleName, final String testC this.testClassName = testClassName; this.expectedIdentifier = expectedIdentifier; this.expectedRepresentations = expectedRepresentations; - this.classUnderTest = new JavaTypeAnalyzer(actualTypeRepresentations); + NormalizedTypeAnalyzerFactory analyzerFactory = testClassSimpleName.contains("Jackson")?new JacksonAnalyzerFactory():new JaxbAnalyzerFactory(); + this.classUnderTest = new JavaTypeAnalyzer(actualTypeRepresentations,analyzerFactory); } @Parameterized.Parameters(name = "{0}") diff --git a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/ResultInterpreterTest.java b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/ResultInterpreterTest.java index 0de1e69..b755174 100644 --- a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/ResultInterpreterTest.java +++ b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/ResultInterpreterTest.java @@ -43,10 +43,12 @@ public class ResultInterpreterTest { private ResultInterpreter classUnderTest; + private NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory; @Before public void setUp() { classUnderTest = new ResultInterpreter(); + normalizedTypeAnalyzerFactory = new JaxbAnalyzerFactory(); } @Test @@ -65,7 +67,7 @@ public void testStandard() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -92,7 +94,7 @@ public void testSubResource() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -111,7 +113,7 @@ public void testRootResource() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -134,7 +136,7 @@ public void testNormalizeGenericEntityNoCollection() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -155,7 +157,7 @@ public void testContentTypes() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -177,7 +179,7 @@ public void testOverrideAnnotationContentType() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -198,7 +200,7 @@ public void testNestedBasePath() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -223,7 +225,7 @@ public void testDefaultStatusCodes() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -250,7 +252,7 @@ public void testNormalize() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -280,7 +282,7 @@ public void testAmbiguousEntityTypes() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } @@ -311,7 +313,7 @@ public void testDescriptions() { final Set results = new HashSet<>(Arrays.asList(appPathResult, resClassResult)); - final Resources actualResult = classUnderTest.interpret(results); + final Resources actualResult = classUnderTest.interpret(results,normalizedTypeAnalyzerFactory); assertEquals(expectedResult, actualResult); } diff --git a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson1.java b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson1.java new file mode 100644 index 0000000..18d6332 --- /dev/null +++ b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson1.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 Sebastian Daschner, sebastian-daschner.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sebastian_daschner.jaxrs_analyzer.analysis.results.testclasses.typeanalyzer; + +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.TypeUtils; +import com.sebastian_daschner.jaxrs_analyzer.model.Types; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeRepresentation; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class TestClassJackson1 { + + private static String PRIVATE_FIELD; + public static String PUBLIC_FIELD; + private String privateField; + protected String protectedField; + public String publicField; + + public String getTest() { + return null; + } + + public int getInt() { + return 0; + } + + public static String getStaticString() { + return null; + } + + public String string() { + return null; + } + + public static Set expectedTypeRepresentations() { + final Map properties = new HashMap<>(); + + properties.put("publicField", TypeUtils.STRING_IDENTIFIER); + properties.put("test", TypeUtils.STRING_IDENTIFIER); + properties.put("int", TypeIdentifier.ofType(Types.PRIMITIVE_INT)); + + return Collections.singleton(TypeRepresentation.ofConcrete(expectedIdentifier(), properties)); + } + + public static TypeIdentifier expectedIdentifier() { + return TypeIdentifier.ofType("Lcom/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson1;"); + } + +} diff --git a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson2.java b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson2.java new file mode 100644 index 0000000..1d3366b --- /dev/null +++ b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson2.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 Sebastian Daschner, sebastian-daschner.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sebastian_daschner.jaxrs_analyzer.analysis.results.testclasses.typeanalyzer; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.TypeUtils; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeRepresentation; + +public class TestClassJackson2 { + + @JsonProperty("prop_one") + private String propOne; + + private String propTwo; + + private String propThree; + + @JsonIgnore + private String propFour; + + @JsonProperty("prop_five") + public String propFive; + + public String getPropOne() { + return propOne; + } + + public void setPropOne(String propOne) { + this.propOne = propOne; + } + + @JsonProperty("prop_2G") + public String getPropTwo() { + return propTwo; + } + + @JsonProperty("prop_2S") + public void setPropTwo(String propTwo) { + this.propTwo = propTwo; + } + + public String getPropThree() { + return propThree; + } + + public void setPropThree(String propThree) { + this.propThree = propThree; + } + + public String getPropFour() { + return propFour; + } + + public void setPropFour(String propFour) { + this.propFour = propFour; + } + + + + public static Set expectedTypeRepresentations() { + final Map properties = new HashMap<>(); + + properties.put("prop_one", TypeUtils.STRING_IDENTIFIER); + properties.put("prop_2G", TypeUtils.STRING_IDENTIFIER); + properties.put("propThree", TypeUtils.STRING_IDENTIFIER); + properties.put("prop_five", TypeUtils.STRING_IDENTIFIER); + + return Collections.singleton(TypeRepresentation.ofConcrete(expectedIdentifier(), properties)); + } + + public static TypeIdentifier expectedIdentifier() { + return TypeIdentifier.ofType("Lcom/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson2;"); + } + +} diff --git a/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson3.java b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson3.java new file mode 100644 index 0000000..687d4b6 --- /dev/null +++ b/src/test/java/com/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson3.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Sebastian Daschner, sebastian-daschner.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sebastian_daschner.jaxrs_analyzer.analysis.results.testclasses.typeanalyzer; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.sebastian_daschner.jaxrs_analyzer.analysis.results.TypeUtils; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier; +import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeRepresentation; + +@JsonAutoDetect(fieldVisibility=Visibility.PROTECTED_AND_PUBLIC,getterVisibility=Visibility.ANY) +public class TestClassJackson3 { + + @SuppressWarnings("unused") + private String propOne; + + String propTwo; + + protected String propThree; + + public String propFour; + + @SuppressWarnings("unused") + private int getInt() { + return 0; + } + + int getInt2() { + return 0; + } + + public static Set expectedTypeRepresentations() { + final Map properties = new HashMap<>(); + properties.put("propThree", TypeUtils.STRING_IDENTIFIER); + properties.put("propFour", TypeUtils.STRING_IDENTIFIER); + properties.put("int", TypeUtils.INT_IDENTIFIER); + properties.put("int2", TypeUtils.INT_IDENTIFIER); + + return Collections.singleton(TypeRepresentation.ofConcrete(expectedIdentifier(), properties)); + } + + public static TypeIdentifier expectedIdentifier() { + return TypeIdentifier.ofType("Lcom/sebastian_daschner/jaxrs_analyzer/analysis/results/testclasses/typeanalyzer/TestClassJackson3;"); + } + +}