From 51be09202d5658e39c1e961fa01d25c957e19005 Mon Sep 17 00:00:00 2001 From: Simo Tripodi Date: Sat, 6 Oct 2018 01:18:25 +0200 Subject: [PATCH 1/3] JOHNZON-192 - Switch johnzon-maven-plugin class generation from manual source writer to a more robust solution --- johnzon-maven-plugin/pom.xml | 13 ++ .../maven/plugin/ExampleToModelMojo.java | 205 ++++++++---------- .../maven/plugin/ExampleToModelMojoTest.java | 144 +++++++++--- 3 files changed, 215 insertions(+), 147 deletions(-) diff --git a/johnzon-maven-plugin/pom.xml b/johnzon-maven-plugin/pom.xml index ea9643fb..b4aa9ad7 100644 --- a/johnzon-maven-plugin/pom.xml +++ b/johnzon-maven-plugin/pom.xml @@ -54,6 +54,12 @@ 3.4 + + com.squareup + javapoet + 1.11.1 + + org.apache.maven maven-plugin-api @@ -69,6 +75,13 @@ maven-plugin-annotations 3.4 + + + com.github.javaparser + javaparser-core + 3.6.24 + test + diff --git a/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java b/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java index 8577b4b0..f63ed15a 100644 --- a/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java +++ b/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java @@ -18,38 +18,44 @@ */ package org.apache.johnzon.maven.plugin; -import org.apache.commons.lang3.StringUtils; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; +import static java.util.Arrays.asList; +import static org.apache.maven.plugins.annotations.LifecyclePhase.GENERATE_SOURCES; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonReader; -import javax.json.JsonReaderFactory; -import javax.json.JsonStructure; -import javax.json.JsonValue; import java.io.File; import java.io.FileReader; -import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Collection; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; -import java.util.TreeSet; -import static java.util.Arrays.asList; -import static org.apache.maven.plugins.annotations.LifecyclePhase.GENERATE_SOURCES; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonReaderFactory; +import javax.json.JsonStructure; +import javax.json.JsonValue; +import javax.lang.model.element.Modifier; + +import org.apache.commons.lang3.StringUtils; +import org.apache.johnzon.mapper.JohnzonProperty; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; + +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; @Mojo(name = "example-to-model", defaultPhase = GENERATE_SOURCES) public class ExampleToModelMojo extends AbstractMojo { @@ -99,7 +105,7 @@ public boolean accept(final File dir, final String name) { } // TODO: unicity of field name, better nested array/object handling - private void generate(final JsonReaderFactory readerFactory, final File source, final Writer writer, final String javaName) throws MojoExecutionException { + private void generate(final JsonReaderFactory readerFactory, final File source, final TypeSpec.Builder targetType) throws MojoExecutionException { JsonReader reader = null; try { reader = readerFactory.createReader(new FileReader(source)); @@ -109,29 +115,12 @@ private void generate(final JsonReaderFactory readerFactory, final File source, } final JsonObject object = JsonObject.class.cast(structure); - final Collection imports = new TreeSet(); - - // while we browse the example tree just store imports as well, avoids a 2 passes processing duplicating imports logic - final StringWriter memBuffer = new StringWriter(); - generateFieldsAndMethods(memBuffer, object, " ", imports); if (header != null) { - writer.write(header); - writer.write('\n'); + targetType.addJavadoc(header); } - writer.write("package " + packageBase + ";\n\n"); - - if (!imports.isEmpty()) { - for (final String imp : imports) { - writer.write("import " + imp + ";\n"); - } - writer.write('\n'); - } - - writer.write("public class " + javaName + " {\n"); - writer.write(memBuffer.toString()); - writer.write("}\n"); + generateFieldsAndMethods(object, targetType); } catch (final IOException e) { throw new MojoExecutionException(e.getMessage(), e); } finally { @@ -141,67 +130,54 @@ private void generate(final JsonReaderFactory readerFactory, final File source, } } - private void generateFieldsAndMethods(final Writer writer, final JsonObject object, final String prefix, - final Collection imports) throws IOException { + private void generateFieldsAndMethods(final JsonObject object, TypeSpec.Builder targetType) { final Map nestedTypes = new TreeMap(); { - final Iterator> iterator = object.entrySet().iterator(); - while (iterator.hasNext()) { - final Map.Entry entry = iterator.next(); + for (final Map.Entry entry : object.entrySet()) { final String key = entry.getKey(); final String fieldName = toJavaFieldName(key); switch (entry.getValue().getValueType()) { case ARRAY: - imports.add("java.util.List"); - handleArray(writer, prefix, nestedTypes, entry.getValue(), key, fieldName, 1, imports); + handleArray(targetType, nestedTypes, entry.getValue(), key, fieldName, 1); break; case OBJECT: final String type = toJavaName(fieldName); nestedTypes.put(type, JsonObject.class.cast(entry.getValue())); - fieldGetSetMethods(writer, key, fieldName, type, prefix, 0, imports); + fieldGetSetMethods(targetType, key, fieldName, type, 0); break; case TRUE: case FALSE: - fieldGetSetMethods(writer, key, fieldName, "Boolean", prefix, 0, imports); + fieldGetSetMethods(targetType, key, fieldName, "Boolean", 0); break; case NUMBER: - fieldGetSetMethods(writer, key, fieldName, "Double", prefix, 0, imports); + fieldGetSetMethods(targetType, key, fieldName, "Double", 0); break; case STRING: - fieldGetSetMethods(writer, key, fieldName, "String", prefix, 0, imports); + fieldGetSetMethods(targetType, key, fieldName, "String", 0); break; case NULL: default: throw new UnsupportedOperationException("Unsupported " + entry.getValue() + "."); } - if (iterator.hasNext()) { - writer.write("\n"); - } } } - if (!object.isEmpty() && !nestedTypes.isEmpty()) { - writer.write("\n"); - } + for (final Map.Entry entry : nestedTypes.entrySet()) { + TypeSpec.Builder nestedType = TypeSpec.classBuilder(entry.getKey()) + .addModifiers( Modifier.PUBLIC, Modifier.STATIC ); - final Iterator> entries = nestedTypes.entrySet().iterator(); - while (entries.hasNext()) { - final Map.Entry entry = entries.next(); - writer.write(prefix + "public static class " + entry.getKey() + " {\n"); - generateFieldsAndMethods(writer, entry.getValue(), " " + prefix, imports); - writer.write(prefix + "}\n"); - if (entries.hasNext()) { - writer.write("\n"); - } + generateFieldsAndMethods(entry.getValue(), nestedType); + + targetType.addType(nestedType.build()); } } - private void handleArray(final Writer writer, final String prefix, + private void handleArray(final TypeSpec.Builder targetType, final Map nestedTypes, final JsonValue value, - final String jsonField,final String fieldName, - final int arrayLevel, - final Collection imports) throws IOException { + final String jsonField, + final String fieldName, + final int arrayLevel) { final JsonArray array = JsonArray.class.cast(value); if (array.size() > 0) { // keep it simple for now - 1 level, we can have an awesome recursive algo later if needed final JsonValue jsonValue = array.get(0); @@ -209,20 +185,20 @@ private void handleArray(final Writer writer, final String prefix, case OBJECT: final String javaName = toJavaName(fieldName); nestedTypes.put(javaName, JsonObject.class.cast(jsonValue)); - fieldGetSetMethods(writer, jsonField, fieldName, javaName, prefix, arrayLevel, imports); + fieldGetSetMethods(targetType, jsonField, fieldName, javaName, arrayLevel); break; case TRUE: case FALSE: - fieldGetSetMethods(writer, jsonField, fieldName, "Boolean", prefix, arrayLevel, imports); + fieldGetSetMethods(targetType, jsonField, fieldName, "Boolean", arrayLevel); break; case NUMBER: - fieldGetSetMethods(writer, jsonField, fieldName, "Double", prefix, arrayLevel, imports); + fieldGetSetMethods(targetType, jsonField, fieldName, "Double", arrayLevel); break; case STRING: - fieldGetSetMethods(writer, jsonField, fieldName, "String", prefix, arrayLevel, imports); + fieldGetSetMethods(targetType, jsonField, fieldName, "String", arrayLevel); break; case ARRAY: - handleArray(writer, prefix, nestedTypes, jsonValue, jsonField, fieldName, arrayLevel + 1, imports); + handleArray(targetType, nestedTypes, jsonValue, jsonField, fieldName, arrayLevel + 1); break; case NULL: default: @@ -233,42 +209,46 @@ private void handleArray(final Writer writer, final String prefix, } } - private void fieldGetSetMethods(final Writer writer, - final String jsonField, final String field, - final String type, final String prefix, final int arrayLevel, - final Collection imports) throws IOException { - final String actualType = buildArrayType(arrayLevel, type); + private void fieldGetSetMethods(final TypeSpec.Builder targetType, + final String jsonField, + final String field, + final String type, + final int arrayLevel) { + final TypeName actualType = buildArrayType(arrayLevel, type); final String actualField = buildValidFieldName(jsonField); final String methodName = StringUtils.capitalize(actualField); - if (!jsonField.equals(field)) { // TODO: add it to imports in eager visitor - imports.add("org.apache.johnzon.mapper.JohnzonProperty"); - writer.append(prefix).append("@JohnzonProperty(\"").append(jsonField).append("\")\n"); + FieldSpec.Builder fieldBuilder = FieldSpec.builder(actualType, actualField, Modifier.PRIVATE); + + if (!jsonField.equals(field)) { + fieldBuilder.addAnnotation(AnnotationSpec.builder(JohnzonProperty.class) + .addMember("value", "$S", jsonField) + .build()); } - writer.append(prefix).append("private ").append(actualType).append(" ").append(actualField).append(";\n"); - writer.append(prefix).append("public ").append(actualType).append(" get").append(methodName).append("() {\n"); - writer.append(prefix).append(" return ").append(actualField).append(";\n"); - writer.append(prefix).append("}\n"); - writer.append(prefix).append("public void set").append(methodName).append("(final ").append(actualType).append(" newValue) {\n"); - writer.append(prefix).append(" this.").append(actualField).append(" = newValue;\n"); - writer.append(prefix).append("}\n"); + targetType.addField(fieldBuilder.build()); + + targetType.addMethod(MethodSpec.methodBuilder("get" + methodName) + .addModifiers(Modifier.PUBLIC) + .returns(actualType) + .addStatement("return $L", actualField) + .build()); + + targetType.addMethod(MethodSpec.methodBuilder("set" + methodName) + .addModifiers(Modifier.PUBLIC) + .addParameter(actualType, actualField, Modifier.FINAL) + .addStatement("this.$1L = $1L", actualField) + .build()); } - private String buildArrayType(final int arrayLevel, final String type) { + private TypeName buildArrayType(final int arrayLevel, final String type) { + ClassName typeArgument = ClassName.bestGuess(type); + if (arrayLevel == 0) { // quick exit - return type; + return typeArgument; } - final StringBuilder builder = new StringBuilder(); - for (int i = 0; i < arrayLevel; i++) { - builder.append("List<"); - } - builder.append(type); - for (int i = 0; i < arrayLevel; i++) { - builder.append(">"); - } - return builder.toString(); + return ParameterizedTypeName.get(ClassName.get(List.class), typeArgument); } private void visit(final JsonStructure structure, final Visitor visitor) { @@ -283,24 +263,15 @@ private void visit(final JsonStructure structure, final Visitor visitor) { private void generateFile(final JsonReaderFactory readerFactory, final File source) throws MojoExecutionException { final String javaName = StringUtils.capitalize(toJavaName(source.getName())); - final String jsonToClass = packageBase + '.' + javaName; - final File outputFile = new File(target, jsonToClass.replace('.', '/') + ".java"); - outputFile.getParentFile().mkdirs(); - FileWriter writer = null; + TypeSpec.Builder targetType = TypeSpec.classBuilder(javaName).addModifiers(Modifier.PUBLIC); + + generate(readerFactory, source, targetType); + try { - writer = new FileWriter(outputFile); - generate(readerFactory, source, writer, javaName); + JavaFile.builder(packageBase, targetType.build()).build().writeTo(target); } catch (IOException e) { throw new MojoExecutionException(e.getMessage(), e); - } finally { - try { - if (writer != null) { - writer.close(); - } - } catch (final IOException e) { - // no-op - } } } diff --git a/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java b/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java index 13e7ec52..23585d0a 100644 --- a/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java +++ b/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java @@ -18,22 +18,29 @@ */ package org.apache.johnzon.maven.plugin; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.codehaus.plexus.util.IOUtil; -import org.junit.Test; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.io.File; -import java.io.FileReader; import java.io.FileWriter; -import java.io.IOException; +import java.util.LinkedList; +import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Test; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.body.AnnotationMemberDeclaration; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; public class ExampleToModelMojoTest { @Test - public void generate() throws MojoFailureException, MojoExecutionException, IOException { + public void generate() throws Exception { final File sourceFolder = new File("target/ExampleToModelMojoTest/source/"); final File targetFolder = new File("target/ExampleToModelMojoTest/target/"); final ExampleToModelMojo mojo = new ExampleToModelMojo() {{ @@ -41,24 +48,22 @@ public void generate() throws MojoFailureException, MojoExecutionException, IOEx target = targetFolder; packageBase = "org.test.apache.johnzon.mojo"; header = - "/*\n" + - " * Licensed to the Apache Software Foundation (ASF) under one\n" + - " * or more contributor license agreements. See the NOTICE file\n" + - " * distributed with this work for additional information\n" + - " * regarding copyright ownership. The ASF licenses this file\n" + - " * to you under the Apache License, Version 2.0 (the\n" + - " * \"License\"); you may not use this file except in compliance\n" + - " * with the License. You may obtain a copy of the License at\n" + - " *\n" + - " * http://www.apache.org/licenses/LICENSE-2.0\n" + - " *\n" + - " * Unless required by applicable law or agreed to in writing,\n" + - " * software distributed under the License is distributed on an\n" + - " * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n" + - " * KIND, either express or implied. See the License for the\n" + - " * specific language governing permissions and limitations\n" + - " * under the License.\n" + - " */"; + "Licensed to the Apache Software Foundation (ASF) under one\n" + + "or more contributor license agreements. See the NOTICE file\n" + + "distributed with this work for additional information\n" + + "regarding copyright ownership. The ASF licenses this file\n" + + "to you under the Apache License, Version 2.0 (the\n" + + "\"License\"); you may not use this file except in compliance\n" + + "with the License. You may obtain a copy of the License at\n" + + "\n" + + " http://www.apache.org/licenses/LICENSE-2.0\n" + + "\n" + + "Unless required by applicable law or agreed to in writing,\n" + + "software distributed under the License is distributed on an\n" + + "\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n" + + "KIND, either express or implied. See the License for the\n" + + "specific language governing permissions and limitations\n" + + "under the License.\n"; }}; sourceFolder.mkdirs(); @@ -109,9 +114,88 @@ public void generate() throws MojoFailureException, MojoExecutionException, IOEx mojo.execute(); final File output = new File(targetFolder, "org/test/apache/johnzon/mojo/SomeValue.java"); + assertTrue(output.exists()); assertTrue(output.isFile()); - assertEquals( - new String(IOUtil.toByteArray(Thread.currentThread().getContextClassLoader().getResourceAsStream("SomeValue.java"))), - new String(IOUtil.toByteArray(new FileReader(output)))); + + File input = new File(getClass().getResource("/SomeValue.java").toURI()); + CompilationUnit expected = JavaParser.parse(input); + + CompilationUnit actual = JavaParser.parse(output); + + assertEquals(expected.getPackageDeclaration(), actual.getPackageDeclaration()); + assertEquals(expected.getImports(), actual.getImports()); + assertEquals(expected.getPrimaryTypeName(), actual.getPrimaryTypeName()); + + //assertEquals( expected.accept( v, arg );, actual ); + CollectorVisitor expectedVisitor = visit(expected); + CollectorVisitor actualVisitor = visit(actual); + + expectedVisitor.getTypes() + .forEach(type -> assertThat(actualVisitor.getTypes(), hasItem(type))); + expectedVisitor.getFields() + .forEach(field -> assertThat(actualVisitor.getFields(), hasItem(field))); + expectedVisitor.getAnnotations() + .forEach(annotation -> assertThat(actualVisitor.getAnnotations(), hasItem(annotation))); + expectedVisitor.getMethods() + .forEach(method -> assertThat(actualVisitor.getMethods(), hasItem(method))); + } + + private static CollectorVisitor visit(CompilationUnit unit) { + CollectorVisitor visitor = new CollectorVisitor(); + unit.accept(visitor, null); + return visitor; + } + + private static final class CollectorVisitor extends VoidVisitorAdapter { + + public List getTypes() { + return types; + } + + private final List fields = new LinkedList<>(); + + private final List annotations = new LinkedList<>(); + + private final List methods = new LinkedList<>(); + + private final List types = new LinkedList<>(); + + public List getFields() { + return fields; + } + + public List getAnnotations() { + return annotations; + } + + public List getMethods() { + return methods; + } + + @Override + public void visit(FieldDeclaration field, Void arg) { + field.getVariables().forEach(current -> fields.add(current.getNameAsString())); + super.visit(field, arg); + } + + @Override + public void visit(AnnotationMemberDeclaration annotation, Void arg) { + annotations.add(annotation.getNameAsString()); + super.visit(annotation, arg); + } + + @Override + public void visit(MethodDeclaration method, Void arg) { + methods.add(method.getNameAsString()); + super.visit(method, arg); + } + + @Override + public void visit(ClassOrInterfaceDeclaration type, Void arg) { + types.add(type.getNameAsString()); + super.visit(type, arg); + } + } + } From 3c259f78a63aa6978d3bc91dcc4d429498a2d031 Mon Sep 17 00:00:00 2001 From: Simo Tripodi Date: Sat, 6 Oct 2018 11:36:54 +0200 Subject: [PATCH 2/3] JOHNZON-192 - Switch johnzon-maven-plugin class generation from manual source writer to a more robust solution stronger typization --- .../maven/plugin/ExampleToModelMojo.java | 28 +++++++++---------- .../maven/plugin/ExampleToModelMojoTest.java | 16 ++++++++++- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java b/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java index f63ed15a..c79fbb20 100644 --- a/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java +++ b/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java @@ -143,17 +143,18 @@ private void generateFieldsAndMethods(final JsonObject object, TypeSpec.Builder case OBJECT: final String type = toJavaName(fieldName); nestedTypes.put(type, JsonObject.class.cast(entry.getValue())); - fieldGetSetMethods(targetType, key, fieldName, type, 0); + final ClassName nestedType = ClassName.bestGuess(type); + fieldGetSetMethods(targetType, key, fieldName, nestedType, 0); break; case TRUE: case FALSE: - fieldGetSetMethods(targetType, key, fieldName, "Boolean", 0); + fieldGetSetMethods(targetType, key, fieldName, ClassName.get(Boolean.class), 0); break; case NUMBER: - fieldGetSetMethods(targetType, key, fieldName, "Double", 0); + fieldGetSetMethods(targetType, key, fieldName, ClassName.get(Double.class), 0); break; case STRING: - fieldGetSetMethods(targetType, key, fieldName, "String", 0); + fieldGetSetMethods(targetType, key, fieldName, ClassName.get(String.class), 0); break; case NULL: default: @@ -185,17 +186,18 @@ private void handleArray(final TypeSpec.Builder targetType, case OBJECT: final String javaName = toJavaName(fieldName); nestedTypes.put(javaName, JsonObject.class.cast(jsonValue)); - fieldGetSetMethods(targetType, jsonField, fieldName, javaName, arrayLevel); + final ClassName javaType = ClassName.bestGuess(javaName); + fieldGetSetMethods(targetType, jsonField, fieldName, javaType, arrayLevel); break; case TRUE: case FALSE: - fieldGetSetMethods(targetType, jsonField, fieldName, "Boolean", arrayLevel); + fieldGetSetMethods(targetType, jsonField, fieldName, ClassName.get(Boolean.class), arrayLevel); break; case NUMBER: - fieldGetSetMethods(targetType, jsonField, fieldName, "Double", arrayLevel); + fieldGetSetMethods(targetType, jsonField, fieldName, ClassName.get(Double.class), arrayLevel); break; case STRING: - fieldGetSetMethods(targetType, jsonField, fieldName, "String", arrayLevel); + fieldGetSetMethods(targetType, jsonField, fieldName, ClassName.get(String.class), arrayLevel); break; case ARRAY: handleArray(targetType, nestedTypes, jsonValue, jsonField, fieldName, arrayLevel + 1); @@ -212,7 +214,7 @@ private void handleArray(final TypeSpec.Builder targetType, private void fieldGetSetMethods(final TypeSpec.Builder targetType, final String jsonField, final String field, - final String type, + final ClassName type, final int arrayLevel) { final TypeName actualType = buildArrayType(arrayLevel, type); final String actualField = buildValidFieldName(jsonField); @@ -241,14 +243,12 @@ private void fieldGetSetMethods(final TypeSpec.Builder targetType, .build()); } - private TypeName buildArrayType(final int arrayLevel, final String type) { - ClassName typeArgument = ClassName.bestGuess(type); - + private TypeName buildArrayType(final int arrayLevel, final ClassName type) { if (arrayLevel == 0) { // quick exit - return typeArgument; + return type; } - return ParameterizedTypeName.get(ClassName.get(List.class), typeArgument); + return ParameterizedTypeName.get(ClassName.get(List.class), type); } private void visit(final JsonStructure structure, final Visitor visitor) { diff --git a/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java b/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java index 23585d0a..b89e202d 100644 --- a/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java +++ b/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java @@ -32,6 +32,7 @@ import com.github.javaparser.JavaParser; import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.ImportDeclaration; import com.github.javaparser.ast.body.AnnotationMemberDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; @@ -123,13 +124,14 @@ public void generate() throws Exception { CompilationUnit actual = JavaParser.parse(output); assertEquals(expected.getPackageDeclaration(), actual.getPackageDeclaration()); - assertEquals(expected.getImports(), actual.getImports()); assertEquals(expected.getPrimaryTypeName(), actual.getPrimaryTypeName()); //assertEquals( expected.accept( v, arg );, actual ); CollectorVisitor expectedVisitor = visit(expected); CollectorVisitor actualVisitor = visit(actual); + expectedVisitor.getImports() + .forEach(expectedImport -> assertThat(actualVisitor.getImports(), hasItem(expectedImport))); expectedVisitor.getTypes() .forEach(type -> assertThat(actualVisitor.getTypes(), hasItem(type))); expectedVisitor.getFields() @@ -152,6 +154,8 @@ public List getTypes() { return types; } + private final List imports = new LinkedList<>(); + private final List fields = new LinkedList<>(); private final List annotations = new LinkedList<>(); @@ -160,6 +164,10 @@ public List getTypes() { private final List types = new LinkedList<>(); + public List getImports() { + return imports; + } + public List getFields() { return fields; } @@ -172,6 +180,12 @@ public List getMethods() { return methods; } + @Override + public void visit(ImportDeclaration importDeclaration, Void arg) { + imports.add(importDeclaration.getNameAsString()); + super.visit(importDeclaration, arg); + } + @Override public void visit(FieldDeclaration field, Void arg) { field.getVariables().forEach(current -> fields.add(current.getNameAsString())); From b5803eaf350a1e66ad62ca5e7103c5bfb9152816 Mon Sep 17 00:00:00 2001 From: Simo Tripodi Date: Sat, 6 Oct 2018 12:34:48 +0200 Subject: [PATCH 3/3] JOHNZON-192 - Switch johnzon-maven-plugin class generation from manual source writer to a more robust solution avoid nested classes of nested classes --- .../maven/plugin/ExampleToModelMojo.java | 41 ++--- .../maven/plugin/ExampleToModelMojoTest.java | 15 +- .../src/test/resources/PrimaryMetric.java | 71 +++++++++ .../src/test/resources/ScorePercentiles.java | 113 ++++++++++++++ .../src/test/resources/SecondaryMetrics.java | 22 +++ .../src/test/resources/SomeValue.java | 147 ------------------ 6 files changed, 240 insertions(+), 169 deletions(-) create mode 100644 johnzon-maven-plugin/src/test/resources/PrimaryMetric.java create mode 100644 johnzon-maven-plugin/src/test/resources/ScorePercentiles.java create mode 100644 johnzon-maven-plugin/src/test/resources/SecondaryMetrics.java diff --git a/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java b/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java index c79fbb20..c8955109 100644 --- a/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java +++ b/johnzon-maven-plugin/src/main/java/org/apache/johnzon/maven/plugin/ExampleToModelMojo.java @@ -104,6 +104,22 @@ public boolean accept(final File dir, final String name) { } } + private void generate(final JsonObject object, final TypeSpec.Builder targetType) throws MojoExecutionException { + targetType.addModifiers(Modifier.PUBLIC); + + if (header != null) { + targetType.addJavadoc(header); + } + + generateFieldsAndMethods(object, targetType); + + try { + JavaFile.builder(packageBase, targetType.build()).build().writeTo(target); + } catch (IOException e) { + throw new MojoExecutionException("An error occurred while serializing class " + targetType, e); + } + } + // TODO: unicity of field name, better nested array/object handling private void generate(final JsonReaderFactory readerFactory, final File source, final TypeSpec.Builder targetType) throws MojoExecutionException { JsonReader reader = null; @@ -116,11 +132,7 @@ private void generate(final JsonReaderFactory readerFactory, final File source, final JsonObject object = JsonObject.class.cast(structure); - if (header != null) { - targetType.addJavadoc(header); - } - - generateFieldsAndMethods(object, targetType); + generate(object, targetType); } catch (final IOException e) { throw new MojoExecutionException(e.getMessage(), e); } finally { @@ -130,7 +142,7 @@ private void generate(final JsonReaderFactory readerFactory, final File source, } } - private void generateFieldsAndMethods(final JsonObject object, TypeSpec.Builder targetType) { + private void generateFieldsAndMethods(final JsonObject object, TypeSpec.Builder targetType) throws MojoExecutionException { final Map nestedTypes = new TreeMap(); { for (final Map.Entry entry : object.entrySet()) { @@ -143,8 +155,7 @@ private void generateFieldsAndMethods(final JsonObject object, TypeSpec.Builder case OBJECT: final String type = toJavaName(fieldName); nestedTypes.put(type, JsonObject.class.cast(entry.getValue())); - final ClassName nestedType = ClassName.bestGuess(type); - fieldGetSetMethods(targetType, key, fieldName, nestedType, 0); + fieldGetSetMethods(targetType, key, fieldName, ClassName.get(packageBase, type), 0); break; case TRUE: case FALSE: @@ -164,12 +175,10 @@ private void generateFieldsAndMethods(final JsonObject object, TypeSpec.Builder } for (final Map.Entry entry : nestedTypes.entrySet()) { - TypeSpec.Builder nestedType = TypeSpec.classBuilder(entry.getKey()) - .addModifiers( Modifier.PUBLIC, Modifier.STATIC ); - - generateFieldsAndMethods(entry.getValue(), nestedType); + ClassName nestedType = ClassName.get(packageBase, entry.getKey()); + TypeSpec.Builder nestedTypeSpec = TypeSpec.classBuilder(nestedType); - targetType.addType(nestedType.build()); + generate(entry.getValue(), nestedTypeSpec); } } @@ -267,12 +276,6 @@ private void generateFile(final JsonReaderFactory readerFactory, final File sour TypeSpec.Builder targetType = TypeSpec.classBuilder(javaName).addModifiers(Modifier.PUBLIC); generate(readerFactory, source, targetType); - - try { - JavaFile.builder(packageBase, targetType.build()).build().writeTo(target); - } catch (IOException e) { - throw new MojoExecutionException(e.getMessage(), e); - } } private String buildValidFieldName(final String jsonField) { diff --git a/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java b/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java index b89e202d..807905aa 100644 --- a/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java +++ b/johnzon-maven-plugin/src/test/java/org/apache/johnzon/maven/plugin/ExampleToModelMojoTest.java @@ -114,11 +114,21 @@ public void generate() throws Exception { mojo.execute(); - final File output = new File(targetFolder, "org/test/apache/johnzon/mojo/SomeValue.java"); + File outputFolder = new File(targetFolder, "org/test/apache/johnzon/mojo"); + assertEquals(4, outputFolder.listFiles().length); + + checkClass(outputFolder, "SomeValue.java"); + checkClass(outputFolder, "PrimaryMetric.java"); + checkClass(outputFolder, "ScorePercentiles.java"); + checkClass(outputFolder, "SecondaryMetrics.java"); + } + + private void checkClass(File outputFolder, String name) throws Exception { + final File output = new File(outputFolder, name); assertTrue(output.exists()); assertTrue(output.isFile()); - File input = new File(getClass().getResource("/SomeValue.java").toURI()); + File input = new File(getClass().getClassLoader().getResource(name).toURI()); CompilationUnit expected = JavaParser.parse(input); CompilationUnit actual = JavaParser.parse(output); @@ -126,7 +136,6 @@ public void generate() throws Exception { assertEquals(expected.getPackageDeclaration(), actual.getPackageDeclaration()); assertEquals(expected.getPrimaryTypeName(), actual.getPrimaryTypeName()); - //assertEquals( expected.accept( v, arg );, actual ); CollectorVisitor expectedVisitor = visit(expected); CollectorVisitor actualVisitor = visit(actual); diff --git a/johnzon-maven-plugin/src/test/resources/PrimaryMetric.java b/johnzon-maven-plugin/src/test/resources/PrimaryMetric.java new file mode 100644 index 00000000..8676b99d --- /dev/null +++ b/johnzon-maven-plugin/src/test/resources/PrimaryMetric.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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/LICENSE-2.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 org.test.apache.johnzon.mojo; + +import java.util.List; + +public class PrimaryMetric { + private Double score; + public Double getScore() { + return score; + } + public void setScore(final Double newValue) { + this.score = newValue; + } + + private Double scoreError; + public Double getScoreError() { + return scoreError; + } + public void setScoreError(final Double newValue) { + this.scoreError = newValue; + } + + private List scoreConfidence; + public List getScoreConfidence() { + return scoreConfidence; + } + public void setScoreConfidence(final List newValue) { + this.scoreConfidence = newValue; + } + + private ScorePercentiles scorePercentiles; + public ScorePercentiles getScorePercentiles() { + return scorePercentiles; + } + public void setScorePercentiles(final ScorePercentiles newValue) { + this.scorePercentiles = newValue; + } + + private String scoreUnit; + public String getScoreUnit() { + return scoreUnit; + } + public void setScoreUnit(final String newValue) { + this.scoreUnit = newValue; + } + + private List> rawData; + public List> getRawData() { + return rawData; + } + public void setRawData(final List> newValue) { + this.rawData = newValue; + } +} \ No newline at end of file diff --git a/johnzon-maven-plugin/src/test/resources/ScorePercentiles.java b/johnzon-maven-plugin/src/test/resources/ScorePercentiles.java new file mode 100644 index 00000000..ebc23d35 --- /dev/null +++ b/johnzon-maven-plugin/src/test/resources/ScorePercentiles.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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/LICENSE-2.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 org.test.apache.johnzon.mojo; + +import org.apache.johnzon.mapper.JohnzonProperty; + +public class ScorePercentiles { + @JohnzonProperty("0.0") + private Double _00; + public Double get_00() { + return _00; + } + public void set_00(final Double newValue) { + this._00 = newValue; + } + + @JohnzonProperty("50.0") + private Double _500; + public Double get_500() { + return _500; + } + public void set_500(final Double newValue) { + this._500 = newValue; + } + + @JohnzonProperty("90.0") + private Double _900; + public Double get_900() { + return _900; + } + public void set_900(final Double newValue) { + this._900 = newValue; + } + + @JohnzonProperty("95.0") + private Double _950; + public Double get_950() { + return _950; + } + public void set_950(final Double newValue) { + this._950 = newValue; + } + + @JohnzonProperty("99.0") + private Double _990; + public Double get_990() { + return _990; + } + public void set_990(final Double newValue) { + this._990 = newValue; + } + + @JohnzonProperty("99.9") + private Double _999; + public Double get_999() { + return _999; + } + public void set_999(final Double newValue) { + this._999 = newValue; + } + + @JohnzonProperty("99.99") + private Double _9999; + public Double get_9999() { + return _9999; + } + public void set_9999(final Double newValue) { + this._9999 = newValue; + } + + @JohnzonProperty("99.999") + private Double _99999; + public Double get_99999() { + return _99999; + } + public void set_99999(final Double newValue) { + this._99999 = newValue; + } + + @JohnzonProperty("99.9999") + private Double _999999; + public Double get_999999() { + return _999999; + } + public void set_999999(final Double newValue) { + this._999999 = newValue; + } + + @JohnzonProperty("100.0") + private Double _1000; + public Double get_1000() { + return _1000; + } + public void set_1000(final Double newValue) { + this._1000 = newValue; + } +} \ No newline at end of file diff --git a/johnzon-maven-plugin/src/test/resources/SecondaryMetrics.java b/johnzon-maven-plugin/src/test/resources/SecondaryMetrics.java new file mode 100644 index 00000000..65cda173 --- /dev/null +++ b/johnzon-maven-plugin/src/test/resources/SecondaryMetrics.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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/LICENSE-2.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 org.test.apache.johnzon.mojo; + +public class SecondaryMetrics { +} \ No newline at end of file diff --git a/johnzon-maven-plugin/src/test/resources/SomeValue.java b/johnzon-maven-plugin/src/test/resources/SomeValue.java index 55f1cdf2..a9448efe 100644 --- a/johnzon-maven-plugin/src/test/resources/SomeValue.java +++ b/johnzon-maven-plugin/src/test/resources/SomeValue.java @@ -18,9 +18,6 @@ */ package org.test.apache.johnzon.mojo; -import java.util.List; -import org.apache.johnzon.mapper.JohnzonProperty; - public class SomeValue { private String benchmark; public String getBenchmark() { @@ -102,148 +99,4 @@ public void setSecondaryMetrics(final SecondaryMetrics newValue) { this.secondaryMetrics = newValue; } - public static class PrimaryMetric { - private Double score; - public Double getScore() { - return score; - } - public void setScore(final Double newValue) { - this.score = newValue; - } - - private Double scoreError; - public Double getScoreError() { - return scoreError; - } - public void setScoreError(final Double newValue) { - this.scoreError = newValue; - } - - private List scoreConfidence; - public List getScoreConfidence() { - return scoreConfidence; - } - public void setScoreConfidence(final List newValue) { - this.scoreConfidence = newValue; - } - - private ScorePercentiles scorePercentiles; - public ScorePercentiles getScorePercentiles() { - return scorePercentiles; - } - public void setScorePercentiles(final ScorePercentiles newValue) { - this.scorePercentiles = newValue; - } - - private String scoreUnit; - public String getScoreUnit() { - return scoreUnit; - } - public void setScoreUnit(final String newValue) { - this.scoreUnit = newValue; - } - - private List> rawData; - public List> getRawData() { - return rawData; - } - public void setRawData(final List> newValue) { - this.rawData = newValue; - } - - public static class ScorePercentiles { - @JohnzonProperty("0.0") - private Double _00; - public Double get_00() { - return _00; - } - public void set_00(final Double newValue) { - this._00 = newValue; - } - - @JohnzonProperty("50.0") - private Double _500; - public Double get_500() { - return _500; - } - public void set_500(final Double newValue) { - this._500 = newValue; - } - - @JohnzonProperty("90.0") - private Double _900; - public Double get_900() { - return _900; - } - public void set_900(final Double newValue) { - this._900 = newValue; - } - - @JohnzonProperty("95.0") - private Double _950; - public Double get_950() { - return _950; - } - public void set_950(final Double newValue) { - this._950 = newValue; - } - - @JohnzonProperty("99.0") - private Double _990; - public Double get_990() { - return _990; - } - public void set_990(final Double newValue) { - this._990 = newValue; - } - - @JohnzonProperty("99.9") - private Double _999; - public Double get_999() { - return _999; - } - public void set_999(final Double newValue) { - this._999 = newValue; - } - - @JohnzonProperty("99.99") - private Double _9999; - public Double get_9999() { - return _9999; - } - public void set_9999(final Double newValue) { - this._9999 = newValue; - } - - @JohnzonProperty("99.999") - private Double _99999; - public Double get_99999() { - return _99999; - } - public void set_99999(final Double newValue) { - this._99999 = newValue; - } - - @JohnzonProperty("99.9999") - private Double _999999; - public Double get_999999() { - return _999999; - } - public void set_999999(final Double newValue) { - this._999999 = newValue; - } - - @JohnzonProperty("100.0") - private Double _1000; - public Double get_1000() { - return _1000; - } - public void set_1000(final Double newValue) { - this._1000 = newValue; - } - } - } - - public static class SecondaryMetrics { - } }