Convert WADL generator to FileGenerator API 03/96603/1
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 23 Jun 2021 10:27:44 +0000 (12:27 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 23 Jun 2021 10:33:20 +0000 (12:33 +0200)
FileGeneratorFactory acts as a proper bootstrap for generators,
and it really pushes us towards a proper generator/template split,
slightly reducing the use of Xtend for non-templating purposes.

JIRA: MDSAL-232
Change-Id: I5414bd154b02a87eb0cfccf776de54f38ca5fab9
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/yang/wadl/generator/WadlGenerator.java
binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/yang/wadl/generator/WadlGeneratorFactory.java [new file with mode: 0644]
binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/yang/wadl/generator/WadlTemplate.xtend [moved from binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/yang/wadl/generator/WadlRestconfGenerator.xtend with 66% similarity]
binding/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/mdsal/binding/yang/wadl/generator/WadlGenTest.java

index 93eaa1ff55bd9489c71c56a4a7fbed008d5b5fa9..0e806959c07ceec4961e61f60f9b4eac087bf752 100644 (file)
@@ -7,53 +7,31 @@
  */
 package org.opendaylight.mdsal.binding.yang.wadl.generator;
 
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.Objects.requireNonNull;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Map;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
 import java.util.Set;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFile;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileLifecycle;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFilePath;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileType;
+import org.opendaylight.yangtools.plugin.generator.api.ModuleResourceResolver;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
-import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
-import org.opendaylight.yangtools.yang2sources.spi.ModuleResourceResolver;
-import org.sonatype.plexus.build.incremental.BuildContext;
-
-public class WadlGenerator implements BasicCodeGenerator, BuildContextAware {
-    private BuildContext buildContext;
-
-    @Override
-    public void setAdditionalConfig(final Map<String, String> additionalConfiguration) {
-        // No-op
-    }
-
-    @Override
-    public void setBuildContext(final BuildContext buildContext) {
-        this.buildContext = requireNonNull(buildContext);
-    }
-
-    @Override
-    public void setResourceBaseDir(final File resourceBaseDir) {
-        // No-op
-    }
 
+final class WadlGenerator implements FileGenerator {
     @Override
-    public Collection<File> generateSources(final EffectiveModelContext context, final File outputBaseDir,
-            final Set<Module> currentModules, final ModuleResourceResolver moduleResourcePathResolver)
-            throws IOException {
-        final File outputDir;
-        if (outputBaseDir == null) {
-            // FIXME: this hard-codes the destination
-            outputDir = new File("target" + File.separator + "generated-sources" + File.separator
-                    + "maven-sal-api-gen" + File.separator + "wadl");
-        } else {
-            outputDir = outputBaseDir;
+    public Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generateFiles(final EffectiveModelContext context,
+            final Set<Module> localModules, final ModuleResourceResolver moduleResourcePathResolver) {
+        final var result = ImmutableTable.<GeneratedFileType, GeneratedFilePath, GeneratedFile>builder();
+
+        for (Module module : localModules) {
+            final CharSequence body = new WadlTemplate(context, module).body();
+            if (body != null) {
+                result.put(GeneratedFileType.RESOURCE, GeneratedFilePath.ofPath(module.getName() + ".wadl"),
+                    GeneratedFile.of(GeneratedFileLifecycle.TRANSIENT, body));
+            }
         }
-
-        checkState(buildContext != null, "BuildContext should have been set");
-        return new WadlRestconfGenerator(buildContext, outputDir).generate(context, currentModules);
+        return result.build();
     }
 }
diff --git a/binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/yang/wadl/generator/WadlGeneratorFactory.java b/binding/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/mdsal/binding/yang/wadl/generator/WadlGeneratorFactory.java
new file mode 100644 (file)
index 0000000..6637dd2
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, s.r.o. 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.yang.wadl.generator;
+
+import java.util.Map;
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.yangtools.plugin.generator.api.AbstractFileGeneratorFactory;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorFactory;
+
+@MetaInfServices(value = FileGeneratorFactory.class)
+public final class WadlGeneratorFactory extends AbstractFileGeneratorFactory {
+    public WadlGeneratorFactory() {
+        super(WadlGenerator.class.getName());
+    }
+
+    @Override
+    public FileGenerator newFileGenerator(final Map<String, String> configuration) {
+        return new WadlGenerator();
+    }
+}
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2021 PANTHEON.tech, s.r.o.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -7,17 +8,10 @@
  */
 package org.opendaylight.mdsal.binding.yang.wadl.generator
 
-import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.io.BufferedWriter
-import java.io.File
-import java.io.IOException
-import java.io.OutputStreamWriter
-import java.nio.charset.StandardCharsets
 import java.util.ArrayList
-import java.util.Collection
-import java.util.HashSet
 import java.util.List
 import org.opendaylight.yangtools.yang.common.XMLNamespace
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
@@ -27,70 +21,37 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
 import org.opendaylight.yangtools.yang.model.api.Module
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.sonatype.plexus.build.incremental.BuildContext
-
-class WadlRestconfGenerator {
-
-    static val Logger LOG = LoggerFactory.getLogger(WadlRestconfGenerator)
 
+final class WadlTemplate {
     static val PATH_DELIMETER = '/'
-    val BuildContext buildContext;
-    val File path
-    var EffectiveModelContext context;
-    var List<DataSchemaNode> configData;
-    var List<DataSchemaNode> operationalData;
-    var Module module;
-    var List<LeafSchemaNode> pathListParams;
 
-    new(BuildContext buildContext, File targetPath) {
-        if (!targetPath.exists) {
-            checkState(targetPath.mkdirs, "Unable to create directory: %s", targetPath);
-        }
-        path = targetPath
-        this.buildContext = buildContext
-    }
+    val EffectiveModelContext context
+    val Module module
+    val List<DataSchemaNode> configData = new ArrayList
+    val List<DataSchemaNode> operationalData = new ArrayList
 
-    def generate(EffectiveModelContext context, Collection<? extends Module> modules) {
-        val result = new HashSet;
-        this.context = context
-        for (module : modules) {
-            val dataContainers = module.childNodes.filter[it|it.listOrContainer]
-            if (!dataContainers.empty || !module.rpcs.nullOrEmpty) {
-                configData = new ArrayList
-                operationalData = new ArrayList
+    var List<LeafSchemaNode> pathListParams
 
-                for (data : dataContainers) {
-                    if (data.configuration) {
-                        configData.add(data)
-                    } else {
-                        operationalData.add(data)
-                    }
-                }
+    new(EffectiveModelContext context, Module module) {
+        this.context = requireNonNull(context)
+        this.module = requireNonNull(module)
 
-                this.module = module
-                val destination = new File(path, '''«module.name».wadl''')
-                var OutputStreamWriter fw
-                var BufferedWriter bw
-                try {
-                    fw = new OutputStreamWriter(buildContext.newFileOutputStream(destination), StandardCharsets.UTF_8)
-                    bw = new BufferedWriter(fw)
-                    bw.append(application);
-                } catch (IOException e) {
-                    LOG.error("Failed to emit file {}", destination, e);
-                } finally {
-                    if (bw !== null) {
-                        bw.close();
-                    }
-                    if (fw !== null) {
-                        fw.close();
-                    }
+        for (child : module.childNodes) {
+            if (child instanceof ContainerSchemaNode || child instanceof ListSchemaNode) {
+                if (child.configuration) {
+                    configData.add(child)
+                } else {
+                    operationalData.add(child)
                 }
-                result.add(destination)
             }
         }
-        return result
+    }
+
+    def body() {
+        if (!module.rpcs.empty || !configData.empty || !operationalData.empty) {
+            return application()
+        }
+        return null
     }
 
     private def application() '''
@@ -160,10 +121,7 @@ class WadlRestconfGenerator {
         if (schemaNode instanceof ListSchemaNode) {
             for (listKey : schemaNode.keyDefinition) {
                 pathListParams.add((schemaNode as DataNodeContainer).getDataChildByName(listKey) as LeafSchemaNode)
-                path.append(PATH_DELIMETER)
-                path.append('{')
-                path.append(listKey.localName)
-                path.append('}')
+                path.append(PATH_DELIMETER).append('{').append(listKey.localName).append('}')
             }
         }
         return path.toString
@@ -177,7 +135,7 @@ class WadlRestconfGenerator {
         «val children = (schemaNode as DataNodeContainer).childNodes.filter[it|it.listOrContainer]»
         «IF config»
             «schemaNode.methodDelete»
-            «schemaNode.mehodPut»
+            «schemaNode.methodPut»
             «FOR child : children»
                 «child.mehodPost»
             «ENDFOR»
@@ -196,7 +154,7 @@ class WadlRestconfGenerator {
         «ENDFOR»
     '''
 
-    private def methodGet(DataSchemaNode schemaNode) '''
+    private static def methodGet(DataSchemaNode schemaNode) '''
         <method name="GET">
             <response>
                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
@@ -204,7 +162,7 @@ class WadlRestconfGenerator {
         </method>
     '''
 
-    private def mehodPut(DataSchemaNode schemaNode) '''
+    private static def methodPut(DataSchemaNode schemaNode) '''
         <method name="PUT">
             <request>
                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
@@ -212,7 +170,7 @@ class WadlRestconfGenerator {
         </method>
     '''
 
-    private def mehodPost(DataSchemaNode schemaNode) '''
+    private static def mehodPost(DataSchemaNode schemaNode) '''
         <method name="POST">
             <request>
                 «representation(schemaNode.QName.namespace, schemaNode.QName.localName)»
@@ -220,7 +178,7 @@ class WadlRestconfGenerator {
         </method>
     '''
 
-    private def methodPostRpc(boolean input, boolean output) '''
+    private static def methodPostRpc(boolean input, boolean output) '''
         <method name="POST">
             «IF input»
             <request>
@@ -235,11 +193,11 @@ class WadlRestconfGenerator {
         </method>
     '''
 
-    private def methodDelete(DataSchemaNode schemaNode) '''
+    private static def methodDelete(DataSchemaNode schemaNode) '''
         <method name="DELETE" />
     '''
 
-    private def representation(XMLNamespace prefix, String name) '''
+    private static def representation(XMLNamespace prefix, String name) '''
         «val elementData = name»
         <representation mediaType="application/xml" element="«elementData»"/>
         <representation mediaType="text/xml" element="«elementData»"/>
@@ -250,8 +208,7 @@ class WadlRestconfGenerator {
 
     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
                 justification = "https://github.com/spotbugs/spotbugs/issues/811")
-    private def boolean isListOrContainer(DataSchemaNode schemaNode) {
-        return (schemaNode instanceof ListSchemaNode || schemaNode instanceof ContainerSchemaNode)
+    private static def boolean isListOrContainer(DataSchemaNode schemaNode) {
+        return schemaNode instanceof ListSchemaNode || schemaNode instanceof ContainerSchemaNode
     }
-
 }
index f3aa38301a6efb803da499421034d9be1edcca02..97758b1fb2b331e574480c160268f1e86d4bc695 100644 (file)
@@ -8,99 +8,25 @@
 package org.opendaylight.mdsal.binding.yang.wadl.generator;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import com.google.common.collect.Table;
 import java.util.Optional;
 import java.util.Set;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFile;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFilePath;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileType;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
-import org.sonatype.plexus.build.incremental.DefaultBuildContext;
 
 public class WadlGenTest {
-
-    private static final String FS = File.separator;
-    private static final String TEST_PATH = "target" + FS + "test" + FS + "site";
-    private static final File GENERATOR_OUTPUT_DIR = new File(TEST_PATH);
-
-    @Before
-    public void init() {
-        if (GENERATOR_OUTPUT_DIR.exists()) {
-            deleteTestDir(GENERATOR_OUTPUT_DIR);
-        }
-        assertTrue(GENERATOR_OUTPUT_DIR.mkdirs());
-    }
-
-    @After
-    public void cleanUp() {
-        if (GENERATOR_OUTPUT_DIR.exists()) {
-            deleteTestDir(GENERATOR_OUTPUT_DIR);
-        }
-    }
-
     @Test
-    public void testListGeneration() throws Exception {
-        final List<File> sourceFiles = getSourceFiles("/wadl-gen");
-        final EffectiveModelContext context = YangParserTestUtils.parseYangFiles(sourceFiles);
+    public void testListGeneration() {
         final WadlGenerator generator = new WadlGenerator();
-        generator.setBuildContext(new DefaultBuildContext());
-        Collection<File> generatedWadlFiles = generator.generateSources(context, GENERATOR_OUTPUT_DIR,
+        final EffectiveModelContext context = YangParserTestUtils.parseYangResourceDirectory("/wadl-gen");
+        Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generatedWadlFiles = generator.generateFiles(context,
             Set.copyOf(context.getModules()), (module, representation) -> Optional.empty());
         assertEquals(3, generatedWadlFiles.size());
-        generatedWadlFiles.forEach(file -> assertTrue(file.exists()));
-    }
-
-    @Test
-    public void testListGenerationWithoutPath() throws Exception {
-        final List<File> sourceFiles = getSourceFiles("/wadl-gen");
-        final EffectiveModelContext context = YangParserTestUtils.parseYangFiles(sourceFiles);
-        final WadlGenerator generator = new WadlGenerator();
-        generator.setBuildContext(new DefaultBuildContext());
-        Collection<File> generatedWadlFiles = generator.generateSources(context, null, Set.copyOf(context.getModules()),
-            (module, representation) -> Optional.empty());
-        assertEquals(3, generatedWadlFiles.size());
-        generatedWadlFiles.forEach(file -> {
-            deleteTestDir(file);
-            assertFalse(file.exists());
-        });
-    }
-
-    private static List<File> getSourceFiles(final String path) throws Exception {
-        final URI resPath = WadlGenTest.class.getResource(path).toURI();
-        final File sourcesDir = new File(resPath);
-        if (!sourcesDir.exists()) {
-            throw new FileNotFoundException("Testing files were not found(" + sourcesDir.getName() + ")");
-        }
-        final List<File> sourceFiles = new ArrayList<>();
-        final File[] fileArray = sourcesDir.listFiles();
-        if (fileArray == null) {
-            throw new IllegalArgumentException("Unable to locate files in " + sourcesDir);
-        }
-        sourceFiles.addAll(Arrays.asList(fileArray));
-        return sourceFiles;
-    }
-
-    private static void deleteTestDir(final File file) {
-        if (file.isDirectory()) {
-            File[] filesToDelete = file.listFiles();
-            if (filesToDelete != null) {
-                for (File f : filesToDelete) {
-                    deleteTestDir(f);
-                }
-            }
-        }
-        if (!file.delete()) {
-            throw new RuntimeException("Failed to clean up after test");
-        }
+        // TODO: more asserts
     }
 }