From 1143b66d151c997973f831f7fb9cdce4b355f299 Mon Sep 17 00:00:00 2001 From: Jakub Toth Date: Thu, 27 Apr 2017 15:12:04 +0200 Subject: [PATCH] Util class for cleaning of generated Java files *comment generateImplementedMethods in builderTemplate, was breaking buil + tests Change-Id: I5880adec4b9629d8a6761b5e75161e89235a9fe5 Signed-off-by: Jakub Toth --- .../mdsal-binding2-java-api-generator/pom.xml | 4 + .../java/api/generator/GeneratorJavaFile.java | 23 +- .../generator/util/JavaCodePrettyPrint.java | 252 ++++++++++++++++++ .../api/generator/GeneratorJavaFileTest.java | 81 ++++++ .../renderers/BuilderRendererTest.java | 6 +- .../base/with_import/test-import.yang | 11 + .../with_import/test-typedef-with-import.yang | 17 ++ 7 files changed, 382 insertions(+), 12 deletions(-) create mode 100644 binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/util/JavaCodePrettyPrint.java create mode 100644 binding2/mdsal-binding2-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/GeneratorJavaFileTest.java rename binding2/mdsal-binding2-java-api-generator/src/{main/test => test/java}/org/opendaylight/mdsal/binding/javav2/java/api/generator/renderers/BuilderRendererTest.java (94%) create mode 100644 binding2/mdsal-binding2-java-api-generator/src/test/resources/base/with_import/test-import.yang create mode 100644 binding2/mdsal-binding2-java-api-generator/src/test/resources/base/with_import/test-typedef-with-import.yang diff --git a/binding2/mdsal-binding2-java-api-generator/pom.xml b/binding2/mdsal-binding2-java-api-generator/pom.xml index bb4985ef59..8ffac7546a 100644 --- a/binding2/mdsal-binding2-java-api-generator/pom.xml +++ b/binding2/mdsal-binding2-java-api-generator/pom.xml @@ -55,6 +55,10 @@ org.opendaylight.mdsal mdsal-binding2-util + + org.opendaylight.yangtools + yang-test-util + junit junit diff --git a/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/GeneratorJavaFile.java b/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/GeneratorJavaFile.java index c09bcf5f30..f245df5fa8 100644 --- a/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/GeneratorJavaFile.java +++ b/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/GeneratorJavaFile.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; +import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.JavaCodePrettyPrint; import org.opendaylight.mdsal.binding.javav2.model.api.CodeGenerator; import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject; import org.opendaylight.mdsal.binding.javav2.model.api.Type; @@ -68,10 +69,10 @@ public final class GeneratorJavaFile { public GeneratorJavaFile(final BuildContext buildContext, final Collection types) { this.buildContext = Preconditions.checkNotNull(buildContext); this.types = Preconditions.checkNotNull(types); - generators.add(new EnumGenerator()); - generators.add(new InterfaceGenerator()); - generators.add(new BuilderGenerator()); - generators.add(new TOGenerator()); + this.generators.add(new EnumGenerator()); + this.generators.add(new InterfaceGenerator()); + this.generators.add(new BuilderGenerator()); + this.generators.add(new TOGenerator()); } /** @@ -90,14 +91,14 @@ public final class GeneratorJavaFile { public List generateToFile(final File generatedSourcesDirectory, final File persistentSourcesDirectory) throws IOException { final List result = new ArrayList<>(); - for (Type type : types) { + for (final Type type : this.types) { if (type != null) { - for (CodeGenerator generator : generators) { + for (final CodeGenerator generator : this.generators) { File generatedJavaFile = null; if (type instanceof GeneratedTransferObject && ((GeneratedTransferObject) type).isUnionTypeBuilder()) { - File packageDir = packageToDirectory(persistentSourcesDirectory, type.getPackageName()); - File file = new File(packageDir, generator.getUnitName(type) + ".java"); + final File packageDir = packageToDirectory(persistentSourcesDirectory, type.getPackageName()); + final File file = new File(packageDir, generator.getUnitName(type) + ".java"); if (!file.exists()) { generatedJavaFile = generateTypeToJavaFile(persistentSourcesDirectory, type, generator); } @@ -188,7 +189,7 @@ public final class GeneratorJavaFile { } if (generator.isAcceptable(type)) { - final String generatedCode = generator.generate(type); + final String generatedCode = JavaCodePrettyPrint.perform(generator.generate(type)); Preconditions.checkState(!generatedCode.isEmpty(), "Generated code should not be empty!"); final File file = new File(packageDir, ((UnitName) generator.getUnitName(type)).getValue() + ".java"); @@ -198,12 +199,12 @@ public final class GeneratorJavaFile { return null; } - try (final OutputStream stream = buildContext.newFileOutputStream(file)) { + try (final OutputStream stream = this.buildContext.newFileOutputStream(file)) { try (final Writer fw = new OutputStreamWriter(stream, StandardCharsets.UTF_8)) { try (final BufferedWriter bw = new BufferedWriter(fw)) { bw.write(generatedCode); } - } catch (IOException e) { + } catch (final IOException e) { LOG.error("Failed to write generate output into {}", file.getPath(), e); throw e; } diff --git a/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/util/JavaCodePrettyPrint.java b/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/util/JavaCodePrettyPrint.java new file mode 100644 index 0000000000..8163dbde27 --- /dev/null +++ b/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/util/JavaCodePrettyPrint.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.mdsal.binding.javav2.java.api.generator.util; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.RegEx; +import org.apache.commons.lang3.StringUtils; + +/** + * Pretty-print utility for generated Java code. + */ +public final class JavaCodePrettyPrint { + + // JDoc + private static final String JDOC_START = "/**"; + private static final String JDOC_PART = " *"; + private static final String JDOC_END = "*/"; + + // Comments + private static final String COMMENTS = "//"; + + // Body + private static final char START_BODY = '{'; + private static final char END_BODY = '}'; + + // Whitespaces + @RegEx + private static final String NEW_LINE_REGEX = "\\r?\\n"; + @RegEx + private static final String WS_REGEX = "\\s+"; + private static final char SPACE = ' '; + private static final char NEW_LINE = '\n'; + + // Indention + private static final int INDENT = 4; + + // Specific keywords + private static final String PACKAGE = "package"; + private static final String IMPORT = "import"; + + // Line + private static final char END_LINE = ';'; + private static final char AT = '@'; + + private JavaCodePrettyPrint() { + throw new UnsupportedOperationException("Util class"); + } + + /** + * Pretty-print generated Java code. + * + * @param unformedJavaFile + * - unformed Java file from generator + * @return formed Java file + */ + public static String perform(final String unformedJavaFile) { + final StringBuilder sb = new StringBuilder(); + String[] splittedByNewLine = unformedJavaFile.split(NEW_LINE_REGEX); + // remove excessive whitespaces + splittedByNewLine = phaseOne(splittedByNewLine); + // merge or divide lines which need it && setup base new lines at the + // end of lines + splittedByNewLine = phaseTwo(splittedByNewLine); + // fix indents + splittedByNewLine = phaseThree(splittedByNewLine); + + for (final String line : splittedByNewLine) { + sb.append(line); + } + return sb.toString(); + } + + /** + * Set up indents. + * + * @param splittedByNewLine + * - cleaned and merged/divided lines + * @return fixed intents in lines + */ + private static String[] phaseThree(final String[] splittedByNewLine) { + int indentCount = 0; + final List lines = new ArrayList<>(); + for (int i = 0; i < splittedByNewLine.length; i++) { + final StringBuilder sb = new StringBuilder(); + indentCount = lineIndent(sb, indentCount, splittedByNewLine[i]); + sb.append(splittedByNewLine[i]); + if ((splittedByNewLine[i].contains(String.valueOf(END_BODY)) + || splittedByNewLine[i].contains(String.valueOf(END_LINE))) && indentCount == 1) { + sb.append(NEW_LINE); + } + lines.add(sb.toString()); + } + + return lines.toArray(new String[lines.size()]); + } + + private static int lineIndent(final StringBuilder sb, final int indentCount, final String line) { + int newIndentCount = indentCount; + if (line.contains(String.valueOf(END_BODY)) && !line.startsWith(JDOC_PART)) { + newIndentCount--; + } + for (int i = 0; i < (newIndentCount * INDENT); i++) { + sb.append(SPACE); + } + + if (line.contains(String.valueOf(START_BODY)) && !line.startsWith(JDOC_PART)) { + newIndentCount++; + } + + return newIndentCount; + } + + /** + * Join or split lines if necessary. + * + * @param splittedByNewLine + * - cleaned lines from whitespaces around them + * @return fixed lines + */ + private static String[] phaseTwo(final String[] splittedByNewLine) { + final List fixedLines = new ArrayList<>(); + + // prepare package part + if (splittedByNewLine[0].startsWith(PACKAGE)) { + fixedLines.add(new StringBuilder(splittedByNewLine[0]).append(NEW_LINE).append(NEW_LINE).toString()); + } + + // prepare imports + int importsEndAt = 1; + for (int i = 1; i < splittedByNewLine.length - 1; i++) { + if (!splittedByNewLine[i + 1].startsWith(IMPORT)) { + fixedLines.add(new StringBuilder(splittedByNewLine[i]).append(NEW_LINE).append(NEW_LINE).toString()); + importsEndAt = i; + break; + } else { + fixedLines.add(new StringBuilder(splittedByNewLine[i]).append(NEW_LINE).toString()); + } + } + + // prepare class + StringBuilder sbLineClass = new StringBuilder(); + int classStartEnd = 0; + for (int i = importsEndAt + 1; i < splittedByNewLine.length; i++) { + i = appendJDoc(splittedByNewLine, fixedLines, i); + if (!splittedByNewLine[i].contains(String.valueOf(START_BODY))) { + sbLineClass.append(splittedByNewLine[i]).append(SPACE); + } else { + fixedLines.add(sbLineClass.append(splittedByNewLine[i]).append(NEW_LINE).append(NEW_LINE).toString()); + classStartEnd = i + 1; + break; + } + } + + sbLineClass = new StringBuilder(); + for(int i = classStartEnd; i < splittedByNewLine.length; i++){ + i = appendJDoc(splittedByNewLine, fixedLines, i); + if (!splittedByNewLine[i].startsWith(COMMENTS) + && !splittedByNewLine[i].endsWith(String.valueOf(END_LINE)) + && !splittedByNewLine[i].endsWith(String.valueOf(START_BODY)) + && !splittedByNewLine[i].endsWith(String.valueOf(END_BODY)) + && !splittedByNewLine[i].startsWith(String.valueOf(AT))) { + for (int j = i; j < splittedByNewLine.length; j++) { + if (!splittedByNewLine[j].contains(String.valueOf(START_BODY))) { + final String str = splittedByNewLine[j]; + sbLineClass.append(str).append(SPACE); + } else { + fixedLines.add(sbLineClass.append(splittedByNewLine[j]).append(NEW_LINE).toString()); + i = j; + break; + } + } + continue; + } + final String splStri = splittedByNewLine[i]; + final String stringSB = String.valueOf(START_BODY); + final String stringEB = String.valueOf(END_BODY); + if (splStri.contains(stringSB) && splStri.endsWith(stringEB)) { + final StringBuilder sb = new StringBuilder(); + for (int j = 0; j < splittedByNewLine[i].length(); j++) { + if (splittedByNewLine[i].charAt(j) == END_BODY) { + sb.append(NEW_LINE); + } + sb.append(splittedByNewLine[i].charAt(j)); + if (splittedByNewLine[i].charAt(j) == START_BODY) { + sb.append(NEW_LINE); + } + } + final String[] split = sb.toString().split(NEW_LINE_REGEX); + for (final String s : split) { + fixedLines.add(new StringBuilder(s).append(NEW_LINE).toString()); + } + continue; + } + fixedLines.add(new StringBuilder(splittedByNewLine[i]).append(NEW_LINE).toString()); + } + + return fixedLines.toArray(new String[fixedLines.size()]); + } + + private static int appendJDoc(final String[] splittedByNewLine, final List fixedLines, int i) { + if (splittedByNewLine[i].contains(JDOC_START)) { + fixedLines.add(new StringBuilder(splittedByNewLine[i]).append(NEW_LINE).toString()); + for (int j = i + 1; j < splittedByNewLine.length - 1; j++) { + if (splittedByNewLine[j].contains(JDOC_END)) { + fixedLines.add(new StringBuilder(SPACE) + .append(SPACE).append(splittedByNewLine[j]).append(NEW_LINE).toString()); + i = j + 1; + break; + } else { + fixedLines.add(new StringBuilder(SPACE) + .append(SPACE).append(splittedByNewLine[j]).append(NEW_LINE).toString()); + } + } + } + return i; + } + + /** + * Remove empty lines and whitespaces adjacent lines. + * + * @param splittedByNewLine + * - lines with whitespaces around them + * @return cleaned lines from whitespaces + */ + private static String[] phaseOne(final String[] splittedByNewLine) { + final List linesWithoutWhitespaces = new ArrayList<>(); + for (final String line : splittedByNewLine) { + if (!StringUtils.isBlank(line)) { + int lineStart = 0; + for (int i = 0; i < line.length(); i++) { + if (StringUtils.isWhitespace(String.valueOf(line.charAt(i)))) { + lineStart++; + } else { + break; + } + } + int lineEnd = line.length() - 1; + while (StringUtils.isWhitespace(String.valueOf(line.charAt(lineEnd)))) { + lineEnd--; + } + linesWithoutWhitespaces.add(line.substring(lineStart, lineEnd + 1)); + } + } + return linesWithoutWhitespaces.toArray(new String[linesWithoutWhitespaces.size()]); + } +} diff --git a/binding2/mdsal-binding2-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/GeneratorJavaFileTest.java b/binding2/mdsal-binding2-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/GeneratorJavaFileTest.java new file mode 100644 index 0000000000..57c7e8d958 --- /dev/null +++ b/binding2/mdsal-binding2-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/GeneratorJavaFileTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.mdsal.binding.javav2.java.api.generator; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import org.opendaylight.mdsal.binding.javav2.generator.api.BindingGenerator; +import org.opendaylight.mdsal.binding.javav2.generator.impl.BindingGeneratorImpl; +import org.opendaylight.mdsal.binding.javav2.model.api.Type; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; +import org.sonatype.plexus.build.incremental.BuildContext; +import org.sonatype.plexus.build.incremental.DefaultBuildContext; + +public class GeneratorJavaFileTest { + + @Test + public void generatedFilesTest() throws Exception { + final SchemaContext context = YangParserTestUtils.parseYangSources("/base/with_import/"); + final BindingGenerator bindingGenerator = new BindingGeneratorImpl(true); + final List types = bindingGenerator.generateTypes(context, context.getModules()); + final BuildContext buildContext = new DefaultBuildContext(); + final GeneratorJavaFile gjf = new GeneratorJavaFile(buildContext, types); + final File persistentSourcesDirectory = + new File(GeneratorJavaFileTest.class.getResource("/base").getPath()); + final File generatedSourcesDirectory = + new File(GeneratorJavaFileTest.class.getResource("/base").getPath()); + final List generateToFile = gjf.generateToFile(generatedSourcesDirectory, persistentSourcesDirectory); + for (final File f : generateToFile) { + Assert.assertNotNull(f); + } + final List files = new ArrayList<>(); + for (final File file : generateToFile) { + BufferedReader br = null; + FileReader fr = null; + try { + fr = new FileReader(file.getAbsolutePath()); + br = new BufferedReader(fr); + final StringBuilder sb = new StringBuilder(); + String sCurrentLine; + while ((sCurrentLine = br.readLine()) != null) { + sb.append(sCurrentLine).append('\n'); + } + files.add(sb.toString()); + } catch (final IOException e) { + e.printStackTrace(); + } finally { + try { + if (br != null) { + br.close(); + } + if (fr != null) { + fr.close(); + } + } catch (final IOException ex) { + ex.printStackTrace(); + } + } + } + + for (final String s : files) { + Assert.assertNotNull(s); + Assert.assertTrue(!s.isEmpty()); + } + + for (final File file2 : generateToFile) { + file2.delete(); + } + } +} diff --git a/binding2/mdsal-binding2-java-api-generator/src/main/test/org/opendaylight/mdsal/binding/javav2/java/api/generator/renderers/BuilderRendererTest.java b/binding2/mdsal-binding2-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/renderers/BuilderRendererTest.java similarity index 94% rename from binding2/mdsal-binding2-java-api-generator/src/main/test/org/opendaylight/mdsal/binding/javav2/java/api/generator/renderers/BuilderRendererTest.java rename to binding2/mdsal-binding2-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/renderers/BuilderRendererTest.java index 25b6b7728c..c8b18d3552 100644 --- a/binding2/mdsal-binding2-java-api-generator/src/main/test/org/opendaylight/mdsal/binding/javav2/java/api/generator/renderers/BuilderRendererTest.java +++ b/binding2/mdsal-binding2-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/javav2/java/api/generator/renderers/BuilderRendererTest.java @@ -11,10 +11,10 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; - import java.util.ArrayList; import java.util.List; import org.junit.Test; +import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTypeBuilderImpl; import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType; import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature; import org.opendaylight.mdsal.binding.javav2.model.api.Type; @@ -123,6 +123,8 @@ public class BuilderRendererTest { final GeneratedType genType = spy(GeneratedType.class); doReturn(TEST).when(genType).getName(); doReturn(TEST).when(genType).getPackageName(); + doReturn(new GeneratedTypeBuilderImpl(new StringBuilder(methodeName).append("test").toString(), methodeName) + .toInstance()).when(genType).getParentTypeForBuilder(); final List listMethodSign = new ArrayList<>(); for (int i = 0; i < 2; i++) { @@ -145,6 +147,8 @@ public class BuilderRendererTest { final GeneratedType genType = spy(GeneratedType.class); doReturn(TEST).when(genType).getName(); doReturn(TEST).when(genType).getPackageName(); + doReturn(new GeneratedTypeBuilderImpl(new StringBuilder(methodeName).append("test").toString(), methodeName) + .toInstance()).when(genType).getParentTypeForBuilder(); final List listMethodSign = new ArrayList<>(); final MethodSignature methSign = mockMethSign(methodeName); diff --git a/binding2/mdsal-binding2-java-api-generator/src/test/resources/base/with_import/test-import.yang b/binding2/mdsal-binding2-java-api-generator/src/test/resources/base/with_import/test-import.yang new file mode 100644 index 0000000000..ac7c31b2fa --- /dev/null +++ b/binding2/mdsal-binding2-java-api-generator/src/test/resources/base/with_import/test-import.yang @@ -0,0 +1,11 @@ +module test-import { + + yang-version 1; + namespace "urn:test:simple:test:import"; + prefix "test-import"; + + revision 2017-04-21; + + container *my-import-%cont { + } +} \ No newline at end of file diff --git a/binding2/mdsal-binding2-java-api-generator/src/test/resources/base/with_import/test-typedef-with-import.yang b/binding2/mdsal-binding2-java-api-generator/src/test/resources/base/with_import/test-typedef-with-import.yang new file mode 100644 index 0000000000..db5caab4f6 --- /dev/null +++ b/binding2/mdsal-binding2-java-api-generator/src/test/resources/base/with_import/test-typedef-with-import.yang @@ -0,0 +1,17 @@ +module test { + + yang-version 1; + namespace "urn:test:simple:test"; + prefix "test"; + + import test-import { prefix "imported-test"; revision-date 2017-04-21; } + + revision 2017-02-06; + + typedef my-type { + type int8; + } + + container *my-cont { + } +} \ No newline at end of file -- 2.36.6