Use plugin-generator-api in yang-maven-plugin 36/52036/77
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 12 Nov 2018 10:54:14 +0000 (11:54 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 2 Nov 2020 10:02:01 +0000 (11:02 +0100)
Rework plugin execution so that we can use both old-style and
new-style plugins by introducing GeneratorTask. This is then
specialized for both worlds.

We rework the multi-version support so that we run reactor assemply
twice if needed -- for each requested mode separately.

JIRA: YANGTOOLS-1147
Change-Id: I494ad899fabfb065c91537019698fdb131e0f1f2
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
22 files changed:
plugin/plugin-generator-api/src/main/java/org/opendaylight/yangtools/plugin/generator/api/GeneratedFileType.java
plugin/yang-maven-plugin-it/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/it/YangToSourcesPluginTestIT.java
plugin/yang-maven-plugin-it/src/test/resources/test-parent/FileGenerator/pom.xml [new file with mode: 0644]
plugin/yang-maven-plugin-it/src/test/resources/test-parent/NoGenerators/pom.xml
plugin/yang-maven-plugin-it/src/test/resources/test-parent/pom.xml
plugin/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/TestFileGenerator.java [new file with mode: 0644]
plugin/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/TestFileGeneratorFactory.java [new file with mode: 0644]
plugin/yang-maven-plugin-spi/src/test/resources/META-INF/services/org.opendaylight.yangtools.plugin.generator.api.FileGeneratorFactory [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/CodeGeneratorTask.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/CodeGeneratorTaskFactory.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ConfigArg.java
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorArg.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorTask.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorTaskFactory.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratorTask.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratorTaskFactory.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ParserModeAware.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ProcessorModuleReactor.java
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojo.java
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java
plugin/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojoTest.java
plugin/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessorTest.java

index 0c53ac55c96d6a10ef65f4138cb9f8abdfb6c33a..ef871484b046c2e7428c07878ee5727891e503d2 100644 (file)
@@ -17,8 +17,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 
 /**
- * Type of generated file. Four most common kinds are captured in {@link #RESOURCE}, {@link #SOURCE},
- * {@link #TEST_RESOURCE} and {@link #TEST_SOURCE}, but others may be externally defined.
+ * Type of generated file. Two most common kinds are captured in {@link #RESOURCE}, {@link #SOURCE}, but others may be
+ * externally defined.
  *
  * <p>
  * Users of {@link FileGenerator} are expected to provide sensible mapping of {@link GeneratedFileType} to their
@@ -41,19 +41,8 @@ public final class GeneratedFileType {
      */
     public static final GeneratedFileType SOURCE = new GeneratedFileType("source");
 
-    /**
-     * A generated test resource file. This file should be part of test resources.
-     */
-    public static final GeneratedFileType TEST_RESOURCE = new GeneratedFileType("test-resource");
-
-    /**
-     * A generated test source file. This file should be part of test sources.
-     */
-    public static final GeneratedFileType TEST_SOURCE = new GeneratedFileType("test-source");
-
     private static final ImmutableMap<String, GeneratedFileType> WELL_KNOWN = ImmutableMap.of(
-        RESOURCE.name(), RESOURCE, TEST_RESOURCE.name(), TEST_RESOURCE,
-        SOURCE.name(), SOURCE, TEST_SOURCE.name(), TEST_SOURCE);
+        RESOURCE.name(), RESOURCE, SOURCE.name(), SOURCE);
 
     private final String name;
 
index d03addaf44b51a41518c2a58fdc30a0f3d5a3a65..99ad946f5b205b682446d10a044e8e75e5e8acef 100644 (file)
@@ -94,7 +94,8 @@ public class YangToSourcesPluginTestIT {
         Verifier vrf = setUp("test-parent/UnknownGenerator/", true);
         vrf.verifyTextInLog("[INFO] yang-to-sources: Code generator instantiated from "
                 + "org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl");
-        vrf.verifyTextInLog("Failed to instantiate code generator unknown");
+        vrf.verifyTextInLog("on project unknown-generator: Failed to find code generator class unknown");
+        vrf.verifyTextInLog("MojoFailureException: Failed to find code generator class unknown");
         vrf.verifyTextInLog("java.lang.ClassNotFoundException: unknown");
     }
 
@@ -152,6 +153,22 @@ public class YangToSourcesPluginTestIT {
         v2.assertFileNotPresent(buildDir + "/classes/META-INF/yang/types3@2013-02-27.yang");
     }
 
+    @Test
+    public void testFileGenerator() throws Exception {
+        Verifier v1 = setUp("test-parent/FileGenerator/", false);
+        v1.executeGoal("clean");
+        v1.executeGoal("package");
+
+        String buildDir = getMavenBuildDirectory(v1);
+
+        v1.assertFilePresent(buildDir + "/generated-sources/"
+            + "org.opendaylight.yangtools.yang2sources.spi.TestFileGenerator/fooGenSource.test");
+        v1.assertFilePresent(buildDir + "/generated-resources/"
+            + "org.opendaylight.yangtools.yang2sources.spi.TestFileGenerator/foo-gen-resource");
+        v1.assertFilePresent(buildDir + "/../src/main/java/fooSource.test");
+        v1.assertFilePresent(buildDir + "/../src/main/resources/foo-resource");
+    }
+
     private static String getMavenBuildDirectory(final Verifier verifier) throws IOException {
         final Properties sp = new Properties();
         final Path path = new File(verifier.getBasedir() + "/it-project.properties").toPath();
diff --git a/plugin/yang-maven-plugin-it/src/test/resources/test-parent/FileGenerator/pom.xml b/plugin/yang-maven-plugin-it/src/test/resources/test-parent/FileGenerator/pom.xml
new file mode 100644 (file)
index 0000000..56d89ce
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2020 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>file-generator</artifactId>
+
+    <properties>
+        <target.dir>${project.build.directory}</target.dir>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <fileGenerators>
+                                <fileGenerator>
+                                    <identifier>org.opendaylight.yangtools.yang2sources.spi.TestFileGenerator</identifier>
+                                    <configuration>
+                                        <prefix>foo</prefix>
+                                    </configuration>
+                                </fileGenerator>
+                            </fileGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>properties-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
index e5340fe467d31d66c0fe4b1218f2161a0abc5e63..759824896eebb92a567e9af8580f553f84b0adf0 100644 (file)
                         </configuration>
                     </execution>
                 </executions>
-                <dependencies>
-                    <dependency>
-                        <groupId>org.opendaylight.yangtools</groupId>
-                        <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${project.version}</version>
-                        <type>test-jar</type>
-                    </dependency>
-                </dependencies>
             </plugin>
         </plugins>
     </build>
index 092470482204c4820d56b91e72c13ffd5aae97c9..114d8e79fc5559356f88212246e6903b46643a26 100644 (file)
@@ -15,6 +15,7 @@
     <modules>
         <module>additional-config</module>
         <module>correct</module>
+        <module>file-generator</module>
         <module>generate-test1</module>
         <module>generate-test2</module>
         <module>generator</module>
diff --git a/plugin/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/TestFileGenerator.java b/plugin/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/TestFileGenerator.java
new file mode 100644 (file)
index 0000000..a8a33a7
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.spi;
+
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+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;
+
+@NonNullByDefault
+final class TestFileGenerator implements FileGenerator {
+    private final String prefix;
+
+    TestFileGenerator(final String prefix) {
+        this.prefix = prefix;
+    }
+
+    @Override
+    public Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generateFiles(final EffectiveModelContext context,
+            final Set<Module> localModules, final ModuleResourceResolver moduleResourcePathResolver) {
+        if (prefix == null) {
+            return ImmutableTable.of();
+        }
+
+        return ImmutableTable.<GeneratedFileType, GeneratedFilePath, GeneratedFile>builder()
+            .put(GeneratedFileType.SOURCE, GeneratedFilePath.ofFilePath(prefix + "Source.test"),
+                GeneratedFile.of(GeneratedFileLifecycle.PERSISTENT, "source"))
+            .put(GeneratedFileType.RESOURCE, GeneratedFilePath.ofFilePath(prefix + "-resource"),
+                GeneratedFile.of(GeneratedFileLifecycle.PERSISTENT, "test-resource"))
+            .put(GeneratedFileType.SOURCE, GeneratedFilePath.ofFilePath(prefix + "GenSource.test"),
+                GeneratedFile.of(GeneratedFileLifecycle.TRANSIENT, "source"))
+            .put(GeneratedFileType.RESOURCE, GeneratedFilePath.ofFilePath(prefix + "-gen-resource"),
+                GeneratedFile.of(GeneratedFileLifecycle.TRANSIENT, "resource"))
+            .build();
+    }
+}
diff --git a/plugin/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/TestFileGeneratorFactory.java b/plugin/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/TestFileGeneratorFactory.java
new file mode 100644 (file)
index 0000000..a8a5b9d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.spi;
+
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.plugin.generator.api.AbstractFileGeneratorFactory;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+
+@NonNullByDefault
+public final class TestFileGeneratorFactory extends AbstractFileGeneratorFactory {
+    public static final String PREFIX = "prefix";
+
+    public TestFileGeneratorFactory() {
+        super(TestFileGenerator.class.getName());
+    }
+
+    @Override
+    public FileGenerator newFileGenerator(final Map<String, String> configuration) {
+        return new TestFileGenerator(configuration.get(PREFIX));
+    }
+}
diff --git a/plugin/yang-maven-plugin-spi/src/test/resources/META-INF/services/org.opendaylight.yangtools.plugin.generator.api.FileGeneratorFactory b/plugin/yang-maven-plugin-spi/src/test/resources/META-INF/services/org.opendaylight.yangtools.plugin.generator.api.FileGeneratorFactory
new file mode 100644 (file)
index 0000000..b3658f4
--- /dev/null
@@ -0,0 +1 @@
+org.opendaylight.yangtools.yang2sources.spi.TestFileGeneratorFactory
diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/CodeGeneratorTask.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/CodeGeneratorTask.java
new file mode 100644 (file)
index 0000000..31409aa
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Stopwatch;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
+import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+@NonNullByDefault
+final class CodeGeneratorTask extends GeneratorTask<CodeGeneratorTaskFactory> {
+    private static final Logger LOG = LoggerFactory.getLogger(CodeGeneratorTask.class);
+
+    private final File outputDir;
+
+    CodeGeneratorTask(final CodeGeneratorTaskFactory factory, final ContextHolder context, final File outputDir) {
+        super(factory, context);
+        this.outputDir = requireNonNull(outputDir);
+    }
+
+    @Override
+    Collection<File> execute(final CodeGeneratorTaskFactory factory, final ContextHolder modelContext,
+            final BuildContext buildContext) throws IOException {
+        final Stopwatch watch = Stopwatch.createStarted();
+        final BasicCodeGenerator gen = factory.generator();
+        final boolean mark;
+        if (gen instanceof BuildContextAware) {
+            ((BuildContextAware)gen).setBuildContext(buildContext);
+            mark = false;
+        } else {
+            mark = true;
+        }
+
+        LOG.debug("Sources will be generated to {}", outputDir);
+        Collection<File> sources = gen.generateSources(modelContext.getContext(), outputDir,
+            modelContext.getYangModules(), modelContext);
+        if (sources == null) {
+            sources = List.of();
+        }
+
+        LOG.debug("Sources generated by {}: {}", gen.getClass(), sources);
+        LOG.info("Sources generated by {}: {} in {}", gen.getClass(), sources.size(), watch);
+
+        if (mark) {
+            sources.forEach(buildContext::refresh);
+        }
+
+        return sources;
+    }
+}
diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/CodeGeneratorTaskFactory.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/CodeGeneratorTaskFactory.java
new file mode 100644 (file)
index 0000000..8ac38cd
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator.ImportResolutionMode;
+import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Bridge to legacy {@link BasicCodeGenerator} generation.
+ *
+ * @author Robert Varga
+ */
+final class CodeGeneratorTaskFactory extends GeneratorTaskFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(CodeGeneratorTaskFactory.class);
+
+    private final @NonNull BasicCodeGenerator gen;
+    private final CodeGeneratorArg cfg;
+
+    private CodeGeneratorTaskFactory(final ImportResolutionMode importMode, final BasicCodeGenerator gen,
+            final CodeGeneratorArg cfg) {
+        super(importMode.toFileGeneratorMode());
+        this.gen = requireNonNull(gen);
+        this.cfg = requireNonNull(cfg);
+    }
+
+    static GeneratorTaskFactory create(final CodeGeneratorArg cfg) throws MojoFailureException {
+        cfg.check();
+
+        final String codegenClass = cfg.getCodeGeneratorClass();
+        final Class<?> clazz;
+        try {
+            clazz = Class.forName(codegenClass);
+        } catch (ClassNotFoundException e) {
+            throw new MojoFailureException("Failed to find code generator class " + codegenClass, e);
+        }
+        final Class<? extends BasicCodeGenerator> typedClass;
+        try {
+            typedClass = clazz.asSubclass(BasicCodeGenerator.class);
+        } catch (ClassCastException e) {
+            throw new MojoFailureException("Code generator " + clazz + " does not implement "
+                + BasicCodeGenerator.class, e);
+        }
+        final Constructor<? extends BasicCodeGenerator> ctor;
+        try {
+            ctor = typedClass.getDeclaredConstructor();
+        } catch (NoSuchMethodException e) {
+            throw new MojoFailureException("Code generator " + clazz
+                + " does not have an accessible no-argument constructor", e);
+        }
+        final @NonNull BasicCodeGenerator gen;
+        try {
+            gen = ctor.newInstance();
+        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+            throw new MojoFailureException("Failed to instantiate code generator " + clazz, e);
+        }
+        LOG.debug("Code generator instantiated from {}", codegenClass);
+
+        ImportResolutionMode importMode = gen.getImportResolutionMode();
+        if (importMode == null) {
+            importMode = ImportResolutionMode.REVISION_EXACT_OR_LATEST;
+        }
+        return new CodeGeneratorTaskFactory(importMode, gen, cfg);
+    }
+
+    @Override
+    CodeGeneratorTask createTask(final MavenProject project, final ContextHolder context) {
+        if (gen instanceof MavenProjectAware) {
+            ((MavenProjectAware)gen).setMavenProject(project);
+        }
+
+        LOG.debug("Project root dir is {}", project.getBasedir());
+        LOG.debug("{} Additional configuration picked up for : {}: {}", YangToSourcesProcessor.LOG_PREFIX,
+            generatorName(), cfg.getAdditionalConfiguration());
+
+        final File outputDir = cfg.getOutputBaseDir(project);
+        project.addCompileSourceRoot(outputDir.getAbsolutePath());
+
+        gen.setAdditionalConfig(cfg.getAdditionalConfiguration());
+        File resourceBaseDir = cfg.getResourceBaseDir(project);
+
+        final Resource res = new Resource();
+        res.setDirectory(resourceBaseDir.getPath());
+        project.addResource(res);
+
+        gen.setResourceBaseDir(resourceBaseDir);
+        LOG.debug("Folder: {} marked as resources for generator: {}", resourceBaseDir, generatorName());
+        return new CodeGeneratorTask(this, context, outputDir);
+    }
+
+    @Override
+    BasicCodeGenerator generator() {
+        return gen;
+    }
+
+    @Override
+    ToStringHelper addToStringProperties(final ToStringHelper helper) {
+        return super.addToStringProperties(helper).add("configuration", cfg);
+    }
+}
index 5e6bb1157dfae1c9f34b5ec14ec55ed93ef131f9..d653cad8c46cdf36277049c629317d68079c8c72 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang2sources.plugin;
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.base.MoreObjects;
 import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
@@ -25,13 +26,13 @@ public abstract class ConfigArg {
     }
 
     public File getOutputBaseDir(final MavenProject project) {
-        if (outputBaseDir == null) {
-            return null;
-        }
         return outputBaseDir.isAbsolute() ? outputBaseDir : new File(project.getBasedir(), outputBaseDir.getPath());
     }
 
-    public abstract void check();
+    public void check() {
+        requireNonNull(outputBaseDir,
+            "outputBaseDir is null. Please provide a valid outputBaseDir value in the pom.xml");
+    }
 
     /**
      * Configuration argument for code generator class and output directory.
@@ -65,6 +66,7 @@ public abstract class ConfigArg {
 
         @Override
         public void check() {
+            super.check();
             requireNonNull(codeGeneratorClass, "codeGeneratorClass for CodeGenerator cannot be null");
         }
 
@@ -85,5 +87,13 @@ public abstract class ConfigArg {
         public Map<String, String> getAdditionalConfiguration() {
             return additionalConfiguration;
         }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).omitNullValues()
+                .add("resourceDir", resourceBaseDir)
+                .add("configuration", additionalConfiguration)
+                .toString();
+        }
     }
 }
diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorArg.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorArg.java
new file mode 100644 (file)
index 0000000..adf4cad
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.plugin;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+
+public final class FileGeneratorArg implements Identifiable<String> {
+    @Parameter
+    private final Map<String, String> configuration = new HashMap<>();
+
+    @Parameter(required = true)
+    private String identifier;
+
+    public FileGeneratorArg() {
+
+    }
+
+    public FileGeneratorArg(final String identifier) {
+        this.identifier = requireNonNull(identifier);
+    }
+
+    public FileGeneratorArg(final String identifier, final Map<String, String> configuration) {
+        this(identifier);
+        this.configuration.putAll(configuration);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return verifyNotNull(identifier);
+    }
+
+    public @NonNull Map<String, String> getConfiguration() {
+        return Collections.unmodifiableMap(configuration);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("id", identifier).add("configuration", configuration).toString();
+    }
+}
diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorTask.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorTask.java
new file mode 100644 (file)
index 0000000..1cd80f4
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.plugin;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Table;
+import com.google.common.collect.Table.Cell;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.maven.model.Build;
+import org.apache.maven.model.Resource;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+final class FileGeneratorTask extends GeneratorTask<FileGeneratorTaskFactory> {
+    private static final Logger LOG = LoggerFactory.getLogger(FileGeneratorTask.class);
+
+    private final Map<GeneratedFileType, File> persistentDirs = new HashMap<>(4);
+    private final Map<GeneratedFileType, File> transientDirs = new HashMap<>(4);
+    private final MavenProject project;
+    private final File buildDir;
+    private final String suffix;
+
+    FileGeneratorTask(final @NonNull FileGeneratorTaskFactory factory, final @NonNull ContextHolder context,
+            final MavenProject project) {
+        super(factory, context);
+
+        final Build build = project.getBuild();
+        final String buildDirectory = build.getDirectory();
+        this.buildDir = new File(buildDirectory);
+        this.suffix = factory.getIdentifier();
+        this.project = project;
+    }
+
+    @Override
+    Collection<File> execute(final FileGeneratorTaskFactory factory, final ContextHolder modelContext,
+            final BuildContext buildContext) throws FileGeneratorException, IOException {
+        // Step one: determine what files are going to be generated
+        final Stopwatch sw = Stopwatch.createStarted();
+        final FileGenerator gen = factory.generator();
+        final Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generatedFiles = gen.generateFiles(
+            modelContext.getContext(), modelContext.getYangModules(), modelContext);
+        LOG.info("{}: Defined {} files in {}", suffix, generatedFiles.size(), sw);
+
+        // Step two: create generation tasks for each target file and group them by parent directory
+        sw.reset().start();
+        final ListMultimap<File, WriteTask> dirs = MultimapBuilder.hashKeys().arrayListValues().build();
+        for (Cell<GeneratedFileType, GeneratedFilePath, GeneratedFile> cell : generatedFiles.cellSet()) {
+            final GeneratedFile file = cell.getValue();
+            final String relativePath = cell.getColumnKey().getPath();
+            final File target;
+            switch (file.getLifecycle()) {
+                case PERSISTENT:
+                    target = new File(persistentPath(cell.getRowKey()), relativePath);
+                    if (target.exists()) {
+                        LOG.debug("Skipping existing persistent {}", target);
+                        continue;
+                    }
+                    break;
+                case TRANSIENT:
+                    target = new File(transientPath(cell.getRowKey()), relativePath);
+                    break;
+                default:
+                    throw new IllegalStateException("Unsupported file type in " + file);
+            }
+
+            dirs.put(target.getParentFile(), new WriteTask(buildContext, target, cell.getValue()));
+        }
+        LOG.info("Sorted {} files into {} directories in {}", dirs.size(), dirs.keySet().size(), sw);
+
+        // Step three: submit parent directory creation tasks (via parallelStream()) and wait for them to complete
+        sw.reset().start();
+        dirs.keySet().parallelStream().forEach(path -> {
+            try {
+                Files.createDirectories(path.toPath());
+            } catch (IOException e) {
+                throw new IllegalStateException("Failed to create " + path, e);
+            }
+        });
+        LOG.debug("Parent directories created in {}", sw);
+
+        // Step four: submit all code generation tasks (via parallelStream()) and wait for them to complete
+        sw.reset().start();
+        final List<File> result = dirs.values().parallelStream()
+                .map(WriteTask::generateFile)
+                .collect(Collectors.toList());
+        LOG.debug("Generated {} files in {}", result.size(), sw);
+
+        return result;
+    }
+
+    private File persistentPath(final GeneratedFileType fileType) throws FileGeneratorException {
+        final File existing = persistentDirs.get(fileType);
+        if (existing != null) {
+            return existing;
+        }
+        final File newDir = persistentDirectory(fileType);
+        verify(persistentDirs.put(fileType, newDir) == null);
+        return newDir;
+    }
+
+    private File transientPath(final GeneratedFileType fileType) throws FileGeneratorException {
+        final File existing = transientDirs.get(fileType);
+        if (existing != null) {
+            return existing;
+        }
+
+        final File newDir = transientDirectory(fileType);
+        verify(transientDirs.put(fileType, newDir) == null);
+        return newDir;
+    }
+
+    private File persistentDirectory(final GeneratedFileType fileType) throws FileGeneratorException {
+        final File ret;
+        if (GeneratedFileType.SOURCE.equals(fileType)) {
+            ret = new File(project.getBuild().getSourceDirectory());
+        } else if (GeneratedFileType.RESOURCE.equals(fileType)) {
+            ret = new File(new File(project.getBuild().getSourceDirectory()).getParentFile(), "resources");
+        } else {
+            throw new FileGeneratorException("Unknown generated file type " + fileType);
+        }
+        return ret;
+    }
+
+    private File transientDirectory(final GeneratedFileType fileType) throws FileGeneratorException {
+        final File ret;
+        if (GeneratedFileType.SOURCE.equals(fileType)) {
+            ret = transientDirectory("generated-sources");
+            project.addCompileSourceRoot(ret.toString());
+        } else if (GeneratedFileType.RESOURCE.equals(fileType)) {
+            ret = transientDirectory("generated-resources");
+            project.addResource(createResouce(ret));
+        } else {
+            throw new FileGeneratorException("Unknown generated file type " + fileType);
+        }
+        return ret;
+    }
+
+    private File transientDirectory(final String component) {
+        return new File(buildDir, subdirFileName(component));
+    }
+
+    private String subdirFileName(final String component) {
+        return component + File.separatorChar + suffix;
+    }
+
+    private static Resource createResouce(final File directory) {
+        final Resource ret = new Resource();
+        ret.setDirectory(directory.toString());
+        return ret;
+    }
+
+    private static final class WriteTask {
+        private final BuildContext buildContext;
+        private final GeneratedFile file;
+        private final File target;
+
+        WriteTask(final BuildContext buildContext, final File target, final GeneratedFile file) {
+            this.buildContext = requireNonNull(buildContext);
+            this.target = requireNonNull(target);
+            this.file = requireNonNull(file);
+        }
+
+        File generateFile() {
+            try (OutputStream stream = buildContext.newFileOutputStream(target)) {
+                file.writeBody(stream);
+            } catch (IOException e) {
+                throw new IllegalStateException("Failed to generate file " + target, e);
+            }
+            return target;
+        }
+    }
+}
diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorTaskFactory.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileGeneratorTaskFactory.java
new file mode 100644 (file)
index 0000000..f38a96e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.plugin;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.apache.maven.project.MavenProject;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorFactory;
+
+/**
+ * Bridge to a {@link FileGenerator} instance.
+ *
+ * @author Robert Varga
+ */
+final class FileGeneratorTaskFactory extends GeneratorTaskFactory implements Identifiable<String> {
+    private final FileGeneratorArg arg;
+    private final FileGenerator gen;
+
+    private FileGeneratorTaskFactory(final FileGenerator gen, final FileGeneratorArg arg) {
+        super(gen.importResolutionMode());
+        this.arg = arg;
+        this.gen = gen;
+    }
+
+    static FileGeneratorTaskFactory of(final FileGeneratorFactory factory, final FileGeneratorArg arg)
+            throws FileGeneratorException {
+        return new FileGeneratorTaskFactory(factory.newFileGenerator(arg.getConfiguration()), arg);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return arg.getIdentifier();
+    }
+
+    @Override
+    FileGeneratorTask createTask(final MavenProject project, final ContextHolder context) {
+        return new FileGeneratorTask(this, context, project);
+    }
+
+    @Override
+    FileGenerator generator() {
+        return gen;
+    }
+
+    @Override
+    ToStringHelper addToStringProperties(final ToStringHelper helper) {
+        return super.addToStringProperties(helper).add("argument", arg);
+    }
+}
diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratorTask.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratorTask.java
new file mode 100644 (file)
index 0000000..52042ef
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+@NonNullByDefault
+abstract class GeneratorTask<T extends GeneratorTaskFactory> extends ParserModeAware {
+    private final ContextHolder context;
+    private final T factory;
+
+    GeneratorTask(final T factory, final ContextHolder context) {
+        this.factory = requireNonNull(factory);
+        this.context = requireNonNull(context);
+    }
+
+    @Override
+    final StatementParserMode parserMode() {
+        return factory.parserMode();
+    }
+
+    final Collection<File> execute(final BuildContext buildContext) throws FileGeneratorException, IOException {
+        return execute(factory, context, buildContext);
+    }
+
+    abstract Collection<File> execute(T factory, ContextHolder modelContext, BuildContext buildContext)
+        throws FileGeneratorException, IOException;
+}
diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratorTaskFactory.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratorTaskFactory.java
new file mode 100644 (file)
index 0000000..ea3df1b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.plugin;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator.ImportResolutionMode;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+
+@NonNullByDefault
+abstract class GeneratorTaskFactory extends ParserModeAware {
+    private final StatementParserMode parserMode;
+
+    GeneratorTaskFactory(final ImportResolutionMode importMode) {
+        switch (importMode) {
+            case REVISION_EXACT_OR_LATEST:
+                parserMode = StatementParserMode.DEFAULT_MODE;
+                break;
+            case SEMVER_LATEST:
+                parserMode = StatementParserMode.SEMVER_MODE;
+                break;
+            default:
+                throw new LinkageError("Unhandled import mode " + importMode);
+        }
+    }
+
+    @Override
+    final StatementParserMode parserMode() {
+        return parserMode;
+    }
+
+    final String generatorName() {
+        return generator().getClass().getName();
+    }
+
+    /**
+     * Create a new {@link GeneratorTask} which will work in scope of specified {@link MavenProject} with the effective
+     * model held in specified {@link ContextHolder}.
+     *
+     * @param project current Maven Project
+     * @param context model generation context
+     */
+    abstract GeneratorTask<?> createTask(MavenProject project, ContextHolder context);
+
+    abstract Object generator();
+
+    ToStringHelper addToStringProperties(final ToStringHelper helper) {
+        return helper.add("generator", generatorName());
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringProperties(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+}
diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ParserModeAware.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ParserModeAware.java
new file mode 100644 (file)
index 0000000..e76af00
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2020 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.yangtools.yang2sources.plugin;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+
+@NonNullByDefault
+abstract class ParserModeAware {
+
+    abstract StatementParserMode parserMode();
+}
index fa1a2353e68b6298e435277af19a5c13159198bb..853eb850e9494198aad7bc7f43075edd35d43bad 100644 (file)
@@ -48,7 +48,7 @@ final class ProcessorModuleReactor {
     private YangParser parser;
 
     ProcessorModuleReactor(final YangParser parser, final Collection<YangTextSchemaSource> modelsInProject,
-        final Collection<ScannedDependency> dependencies) {
+            final Collection<ScannedDependency> dependencies) {
         this.parser = requireNonNull(parser);
         this.modelsInProject = Maps.uniqueIndex(modelsInProject, YangTextSchemaSource::getIdentifier);
         this.dependencies = ImmutableList.copyOf(dependencies);
index 0974a773e32f2fd894da2138e1ff3a4edea092f9..17ca037022d676dafc3a4ff05ac06102c8eaa9a1 100644 (file)
@@ -14,7 +14,6 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.File;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import org.apache.maven.artifact.repository.ArtifactRepository;
@@ -28,7 +27,9 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.repository.RepositorySystem;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
 import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
 import org.sonatype.plexus.build.incremental.BuildContext;
@@ -61,6 +62,13 @@ public final class YangToSourcesMojo extends AbstractMojo {
     @Parameter(required = false)
     private CodeGeneratorArg[] codeGenerators;
 
+    /**
+     * {@link FileGenerator} instances resolved via ServiceLoader can hold additional configuration, which details
+     * how they are executed.
+     */
+    @Parameter(required = false)
+    private FileGeneratorArg[] fileGenerators;
+
     /**
      * Source directory that will be recursively searched for yang files (ending
      * with .yang suffix).
@@ -97,6 +105,10 @@ public final class YangToSourcesMojo extends AbstractMojo {
     @Parameter(property = "yang.skip")
     private String yangSkip;
 
+    @Parameter(defaultValue = "DEFAULT_MODE")
+    @Deprecated(forRemoval = true)
+    private StatementParserMode parserMode;
+
     public YangToSourcesMojo() {
 
     }
@@ -116,26 +128,19 @@ public final class YangToSourcesMojo extends AbstractMojo {
         Util.checkClasspath(project, repoSystem, localRepository, remoteRepos);
 
         if (yangToSourcesProcessor == null) {
-            List<CodeGeneratorArg> codeGeneratorArgs = processCodeGenerators(codeGenerators);
-
             // defaults to ${basedir}/src/main/yang
             File yangFilesRootFile = processYangFilesRootDir(yangFilesRootDir, project.getBasedir());
             Collection<File> excludedFiles = processExcludeFiles(excludeFiles, yangFilesRootFile);
 
             yangToSourcesProcessor = new YangToSourcesProcessor(buildContext, yangFilesRootFile,
-                    excludedFiles, codeGeneratorArgs, project, inspectDependencies);
+                    excludedFiles, arrayToList(codeGenerators), arrayToList(fileGenerators), project,
+                    inspectDependencies);
         }
         yangToSourcesProcessor.conditionalExecute("true".equals(yangSkip));
     }
 
-    private static List<CodeGeneratorArg> processCodeGenerators(final CodeGeneratorArg[] codeGenerators) {
-        List<CodeGeneratorArg> codeGeneratorArgs;
-        if (codeGenerators == null) {
-            codeGeneratorArgs = Collections.emptyList();
-        } else {
-            codeGeneratorArgs = Arrays.asList(codeGenerators);
-        }
-        return codeGeneratorArgs;
+    private static <T> List<T> arrayToList(final T[] array) {
+        return array == null ? ImmutableList.of() : Arrays.asList(array);
     }
 
     private static File processYangFilesRootDir(final String yangFilesRootDir, final File baseDir) {
@@ -160,5 +165,4 @@ public final class YangToSourcesMojo extends AbstractMojo {
 
         return Collections2.transform(Arrays.asList(excludeFiles), f -> new File(baseDir, f));
     }
-
 }
index 9cf4412984279df3daff002dcb526abfcf7469e5..509e0f0a4ce645330d5688ee91ca8664e8f38c21 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.yangtools.yang2sources.plugin;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
@@ -16,15 +15,14 @@ import com.google.common.base.Stopwatch;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Maps;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.AbstractMap.SimpleImmutableEntry;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -36,6 +34,8 @@ import java.util.stream.Collectors;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.project.MavenProject;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorFactory;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
 import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
@@ -46,10 +46,6 @@ import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRSchemaSource;
 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToIRTransformer;
 import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
-import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
-import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator.ImportResolutionMode;
-import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
-import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonatype.plexus.build.incremental.BuildContext;
@@ -77,18 +73,21 @@ class YangToSourcesProcessor {
     private final File yangFilesRootDir;
     private final Set<File> excludedFiles;
     private final List<CodeGeneratorArg> codeGeneratorArgs;
+    private final Map<String, FileGeneratorArg> fileGeneratorArgs;
     private final MavenProject project;
     private final boolean inspectDependencies;
     private final BuildContext buildContext;
     private final YangProvider yangProvider;
 
     private YangToSourcesProcessor(final BuildContext buildContext, final File yangFilesRootDir,
-            final Collection<File> excludedFiles, final List<CodeGeneratorArg> codeGenerators,
+            final Collection<File> excludedFiles, final List<CodeGeneratorArg> codeGeneratorArgs,
+            final List<FileGeneratorArg> fileGeneratorsArgs,
             final MavenProject project, final boolean inspectDependencies, final YangProvider yangProvider) {
         this.buildContext = requireNonNull(buildContext, "buildContext");
         this.yangFilesRootDir = requireNonNull(yangFilesRootDir, "yangFilesRootDir");
         this.excludedFiles = ImmutableSet.copyOf(excludedFiles);
-        this.codeGeneratorArgs = ImmutableList.copyOf(codeGenerators);
+        this.codeGeneratorArgs = ImmutableList.copyOf(codeGeneratorArgs);
+        this.fileGeneratorArgs = Maps.uniqueIndex(fileGeneratorsArgs, FileGeneratorArg::getIdentifier);
         this.project = requireNonNull(project);
         this.inspectDependencies = inspectDependencies;
         this.yangProvider = requireNonNull(yangProvider);
@@ -99,15 +98,16 @@ class YangToSourcesProcessor {
     YangToSourcesProcessor(final File yangFilesRootDir, final Collection<File> excludedFiles,
             final List<CodeGeneratorArg> codeGenerators, final MavenProject project, final boolean inspectDependencies,
             final YangProvider yangProvider) {
-        this(new DefaultBuildContext(), yangFilesRootDir, excludedFiles, codeGenerators, project,
-                inspectDependencies, yangProvider);
+        this(new DefaultBuildContext(), yangFilesRootDir, excludedFiles, codeGenerators, ImmutableList.of(),
+            project, inspectDependencies, yangProvider);
     }
 
     YangToSourcesProcessor(final BuildContext buildContext, final File yangFilesRootDir,
                 final Collection<File> excludedFiles, final List<CodeGeneratorArg> codeGenerators,
-                final MavenProject project, final boolean inspectDependencies) {
-        this(buildContext, yangFilesRootDir, excludedFiles, codeGenerators, project, inspectDependencies,
-            YangProvider.getInstance());
+                final List<FileGeneratorArg> fileGenerators, final MavenProject project,
+                final boolean inspectDependencies) {
+        this(buildContext, yangFilesRootDir, excludedFiles, codeGenerators, fileGenerators, project,
+            inspectDependencies, YangProvider.getInstance());
     }
 
     public void execute() throws MojoExecutionException, MojoFailureException {
@@ -134,38 +134,51 @@ class YangToSourcesProcessor {
         }
 
         // We need to instantiate all code generators to determine required import resolution mode
-        final List<Entry<CodeGeneratorArg, BasicCodeGenerator>> codeGenerators = instantiateGenerators();
-        final StatementParserMode importMode = determineRequiredImportMode(codeGenerators);
-        final Optional<ProcessorModuleReactor> optReactor = createReactor(importMode, yangFilesInProject);
-        if (!optReactor.isPresent()) {
+        final List<GeneratorTaskFactory> codeGenerators = instantiateGenerators();
+        if (codeGenerators.isEmpty()) {
+            LOG.warn("{} No code generators provided", LOG_PREFIX);
             return;
         }
 
-        final ProcessorModuleReactor reactor = optReactor.get();
-        if (!skip) {
-            final Stopwatch watch = Stopwatch.createStarted();
-            final ContextHolder holder;
+        final Set<StatementParserMode> parserModes = codeGenerators.stream()
+            .map(GeneratorTaskFactory::parserMode)
+            .collect(Collectors.toUnmodifiableSet());
+
+        // FIXME: store these files into state, so that we can verify/clean up
+        final Builder<File> files = ImmutableSet.builder();
+        for (StatementParserMode parserMode : parserModes) {
+            final Optional<ProcessorModuleReactor> optReactor = createReactor(yangFilesInProject, parserMode);
+            if (optReactor.isPresent()) {
+                final ProcessorModuleReactor reactor = optReactor.orElseThrow();
+
+                if (!skip) {
+                    final Stopwatch watch = Stopwatch.createStarted();
+                    final ContextHolder holder;
+
+                    try {
+                        holder = reactor.toContext();
+                    } catch (YangParserException e) {
+                        throw new MojoFailureException("Failed to process reactor " + reactor, e);
+                    } catch (IOException e) {
+                        throw new MojoExecutionException("Failed to read reactor " + reactor, e);
+                    }
+
+                    LOG.info("{} {} YANG models processed in {}", LOG_PREFIX, holder.getContext().getModules().size(),
+                        watch);
+                    files.addAll(generateSources(holder, codeGenerators, parserMode));
+                } else {
+                    LOG.info("{} Skipping YANG code generation because property yang.skip is true", LOG_PREFIX);
+                }
 
-            try {
-                holder = reactor.toContext();
-            } catch (YangParserException e) {
-                throw new MojoFailureException("Failed to process reactor " + reactor, e);
-            } catch (IOException e) {
-                throw new MojoExecutionException("Failed to read reactor " + reactor, e);
+                // FIXME: this is not right: we should be generating the models exactly once!
+                // add META_INF/yang
+                final Collection<YangTextSchemaSource> models = reactor.getModelsInProject();
+                try {
+                    yangProvider.addYangsToMetaInf(project, models);
+                } catch (IOException e) {
+                    throw new MojoExecutionException("Failed write model files for " + models, e);
+                }
             }
-
-            LOG.info("{} {} YANG models processed in {}", LOG_PREFIX, holder.getContext().getModules().size(), watch);
-            generateSources(holder, codeGenerators);
-        } else {
-            LOG.info("{} Skipping YANG code generation because property yang.skip is true", LOG_PREFIX);
-        }
-
-        // add META_INF/yang
-        final Collection<YangTextSchemaSource> models = reactor.getModelsInProject();
-        try {
-            yangProvider.addYangsToMetaInf(project, models);
-        } catch (IOException e) {
-            throw new MojoExecutionException("Failed write model files for " + models, e);
         }
 
         // add META_INF/services
@@ -175,111 +188,89 @@ class YangToSourcesProcessor {
             META_INF_YANG_SERVICES_STRING_JAR);
     }
 
-    private static StatementParserMode determineRequiredImportMode(
-            final Collection<Entry<CodeGeneratorArg, BasicCodeGenerator>> codeGenerators) throws MojoFailureException {
-        ImportResolutionMode requestedMode = null;
-        BasicCodeGenerator requestingGenerator = null;
-
-        for (Entry<CodeGeneratorArg, BasicCodeGenerator> entry : codeGenerators) {
-            final BasicCodeGenerator generator = entry.getValue();
-            final ImportResolutionMode mode = generator.getImportResolutionMode();
-            if (mode == null) {
-                // the generator does not care about the mode
-                continue;
-            }
-
-            if (requestedMode == null) {
-                // No mode yet, we have just determined it
-                requestedMode = mode;
-                requestingGenerator = generator;
-                continue;
-            }
-
-            if (mode != requestedMode) {
-                throw new MojoFailureException(String.format(
-                    "Import resolution mode conflict between %s (%s) and %s (%s)", requestingGenerator, requestedMode,
-                    generator, mode));
-            }
-        }
-
-        if (requestedMode == null) {
-            return StatementParserMode.DEFAULT_MODE;
-        }
-        switch (requestedMode) {
-            case REVISION_EXACT_OR_LATEST:
-                return StatementParserMode.DEFAULT_MODE;
-            case SEMVER_LATEST:
-                return StatementParserMode.SEMVER_MODE;
-            default:
-                throw new IllegalStateException("Unhandled import resolution mode " + requestedMode);
+    private List<GeneratorTaskFactory> instantiateGenerators() throws MojoExecutionException, MojoFailureException {
+        final List<GeneratorTaskFactory> generators = new ArrayList<>(codeGeneratorArgs.size());
+        for (CodeGeneratorArg arg : codeGeneratorArgs) {
+            generators.add(CodeGeneratorTaskFactory.create(arg));
+            LOG.info("{} Code generator instantiated from {}", LOG_PREFIX, arg.getCodeGeneratorClass());
         }
-    }
 
-    private List<Entry<CodeGeneratorArg, BasicCodeGenerator>> instantiateGenerators() throws MojoExecutionException {
-        final List<Entry<CodeGeneratorArg, BasicCodeGenerator>> generators = new ArrayList<>(codeGeneratorArgs.size());
-        for (CodeGeneratorArg arg : codeGeneratorArgs) {
-            arg.check();
+        // Search for available FileGenerator implementations
+        final Map<String, FileGeneratorFactory> factories = Maps.uniqueIndex(
+            ServiceLoader.load(FileGeneratorFactory.class), FileGeneratorFactory::getIdentifier);
+
+        // Assign instantiate FileGenerators with appropriate configurate
+        for (Entry<String, FileGeneratorFactory> entry : factories.entrySet()) {
+            final String id = entry.getKey();
+            FileGeneratorArg arg = fileGeneratorArgs.get(id);
+            if (arg == null) {
+                LOG.debug("{} No configuration for {}, using empty", LOG_PREFIX, id);
+                arg = new FileGeneratorArg(id);
+            }
 
-            final BasicCodeGenerator generator;
             try {
-                generator = getInstance(arg.getCodeGeneratorClass(), BasicCodeGenerator.class);
-            } catch (ReflectiveOperationException e) {
-                throw new MojoExecutionException("Failed to instantiate code generator "
-                        + arg.getCodeGeneratorClass(), e);
+                generators.add(FileGeneratorTaskFactory.of(entry.getValue(), arg));
+            } catch (FileGeneratorException e) {
+                throw new MojoExecutionException("File generator " + id + " failed", e);
             }
-
-            LOG.info("{} Code generator instantiated from {}", LOG_PREFIX, arg.getCodeGeneratorClass());
-            generators.add(new SimpleImmutableEntry<>(arg, generator));
+            LOG.info("{} Code generator {} instantiated", LOG_PREFIX, id);
         }
 
         return generators;
     }
 
     @SuppressWarnings("checkstyle:illegalCatch")
-    private Optional<ProcessorModuleReactor> createReactor(final StatementParserMode parserMode,
-            final List<File> yangFilesInProject) throws MojoExecutionException {
+    private Optional<ProcessorModuleReactor> createReactor(final List<File> yangFilesInProject,
+            final StatementParserMode parserMode) throws MojoExecutionException {
         LOG.info("{} Inspecting {}", LOG_PREFIX, yangFilesRootDir);
 
-        try {
-            final Collection<File> allFiles = new ArrayList<>(yangFilesInProject);
-            final Collection<ScannedDependency> dependencies;
-            if (inspectDependencies) {
-                dependencies = new ArrayList<>();
-                final Stopwatch watch = Stopwatch.createStarted();
+        final Collection<File> allFiles = new ArrayList<>(yangFilesInProject);
+        final Collection<ScannedDependency> dependencies;
+        if (inspectDependencies) {
+            dependencies = new ArrayList<>();
+            final Stopwatch watch = Stopwatch.createStarted();
+
+            try {
                 ScannedDependency.scanDependencies(project).forEach(dep -> {
                     allFiles.add(dep.file());
                     dependencies.add(dep);
                 });
-                LOG.info("{} Found {} dependencies in {}", LOG_PREFIX, dependencies.size(), watch);
-            } else {
-                dependencies = ImmutableList.of();
-            }
-
-            /*
-             * Check if any of the listed files changed. If no changes occurred,
-             * simply return null, which indicates and of execution.
-             */
-            final Stopwatch watch = Stopwatch.createStarted();
-            if (!allFiles.stream().anyMatch(buildContext::hasDelta)) {
-                LOG.info("{} None of {} input files changed", LOG_PREFIX, allFiles.size());
-                return Optional.empty();
+            } catch (IOException e) {
+                LOG.error("{} Failed to scan dependencies", LOG_PREFIX, e);
+                throw new MojoExecutionException(LOG_PREFIX + " Failed to scan dependencies ", e);
             }
+            LOG.info("{} Found {} dependencies in {}", LOG_PREFIX, dependencies.size(), watch);
+        } else {
+            dependencies = ImmutableList.of();
+        }
 
-            final YangParser parser = parserFactory.createParser(parserMode);
-            final List<YangTextSchemaSource> sourcesInProject = new ArrayList<>(yangFilesInProject.size());
+        /*
+         * Check if any of the listed files changed. If no changes occurred, simply return empty, which indicates
+         * end of execution.
+         */
+        final Stopwatch watch = Stopwatch.createStarted();
+        if (!allFiles.stream().anyMatch(buildContext::hasDelta)) {
+            LOG.info("{} None of {} input files changed", LOG_PREFIX, allFiles.size());
+            return Optional.empty();
+        }
 
+        try {
             final List<Entry<YangTextSchemaSource, IRSchemaSource>> parsed = yangFilesInProject.parallelStream()
                     .map(file -> {
                         final YangTextSchemaSource textSource = YangTextSchemaSource.forFile(file);
                         try {
-                            return new SimpleImmutableEntry<>(textSource,
-                                    TextToIRTransformer.transformText(textSource));
+                            return Map.entry(textSource,TextToIRTransformer.transformText(textSource));
                         } catch (YangSyntaxErrorException | IOException e) {
                             throw new IllegalArgumentException("Failed to parse " + file, e);
                         }
                     })
                     .collect(Collectors.toList());
 
+            // FIXME: all of the above checks need to be done just once
+
+
+            final List<YangTextSchemaSource> sourcesInProject = new ArrayList<>(yangFilesInProject.size());
+            final YangParser parser = parserFactory.createParser(parserMode);
             for (final Entry<YangTextSchemaSource, IRSchemaSource> entry : parsed) {
                 final YangTextSchemaSource textSource = entry.getKey();
                 final IRSchemaSource astSource = entry.getValue();
@@ -328,85 +319,31 @@ class YangToSourcesProcessor {
     /**
      * Call generate on every generator from plugin configuration.
      */
-    @SuppressWarnings("checkstyle:illegalCatch")
-    private void generateSources(final ContextHolder context,
-            final Collection<Entry<CodeGeneratorArg, BasicCodeGenerator>> generators) throws MojoFailureException {
-        if (generators.isEmpty()) {
-            LOG.warn("{} No code generators provided", LOG_PREFIX);
-            return;
-        }
+    private Set<File> generateSources(final ContextHolder context, final Collection<GeneratorTaskFactory> generators,
+            final StatementParserMode parserMode) throws MojoFailureException {
+        final Builder<File> allFiles = ImmutableSet.builder();
+        for (GeneratorTaskFactory factory : generators) {
+            if (!parserMode.equals(factory.parserMode())) {
+                continue;
+            }
 
-        final Map<String, String> thrown = new HashMap<>();
-        for (Entry<CodeGeneratorArg, BasicCodeGenerator> entry : generators) {
-            final String codeGeneratorClass = entry.getKey().getCodeGeneratorClass();
+            final Stopwatch sw = Stopwatch.createStarted();
+            final GeneratorTask<?> task = factory.createTask(project, context);
+            LOG.debug("{} Task {} initialized in {}", LOG_PREFIX, task, sw);
 
+            final Collection<File> files;
             try {
-                generateSourcesWithOneGenerator(context, entry.getKey(), entry.getValue());
-            } catch (Exception e) {
-                // try other generators, exception will be thrown after
-                LOG.error("{} Unable to generate sources with {} generator", LOG_PREFIX, codeGeneratorClass, e);
-                thrown.put(codeGeneratorClass, e.getClass().getCanonicalName());
+                files = task.execute(buildContext);
+            } catch (FileGeneratorException | IOException e) {
+                throw new MojoFailureException(LOG_PREFIX + " Generator " + factory + " failed", e);
             }
-        }
 
-        if (!thrown.isEmpty()) {
-            LOG.error("{} One or more code generators failed, including failed list(generatorClass=exception) {}",
-                LOG_PREFIX, thrown);
-            throw new MojoFailureException(LOG_PREFIX
-                + " One or more code generators failed, including failed list(generatorClass=exception) " + thrown);
+            LOG.debug("{} Sources generated by {}: {}", LOG_PREFIX, factory.generatorName(), files);
+            LOG.info("{} Sources generated by {}: {} in {}", LOG_PREFIX, factory.generatorName(),
+                files == null ? 0 : files.size(), sw);
+            allFiles.addAll(files);
         }
-    }
-
-    /**
-     * Complete initialization of a code generator and invoke it.
-     */
-    private void generateSourcesWithOneGenerator(final ContextHolder context, final CodeGeneratorArg codeGeneratorCfg,
-            final BasicCodeGenerator codeGenerator) throws IOException {
 
-        final File outputDir = requireNonNull(codeGeneratorCfg.getOutputBaseDir(project),
-            "outputBaseDir is null. Please provide a valid outputBaseDir value in pom.xml");
-
-        project.addCompileSourceRoot(outputDir.getAbsolutePath());
-
-        LOG.info("{} Sources will be generated to {}", LOG_PREFIX, outputDir);
-        LOG.debug("{} Project root dir is {}", LOG_PREFIX, project.getBasedir());
-        LOG.debug("{} Additional configuration picked up for : {}: {}", LOG_PREFIX, codeGeneratorCfg
-                        .getCodeGeneratorClass(), codeGeneratorCfg.getAdditionalConfiguration());
-
-        if (codeGenerator instanceof BuildContextAware) {
-            ((BuildContextAware)codeGenerator).setBuildContext(buildContext);
-        }
-        if (codeGenerator instanceof MavenProjectAware) {
-            ((MavenProjectAware)codeGenerator).setMavenProject(project);
-        }
-        codeGenerator.setAdditionalConfig(codeGeneratorCfg.getAdditionalConfiguration());
-
-        File resourceBaseDir = codeGeneratorCfg.getResourceBaseDir(project);
-        YangProvider.setResource(resourceBaseDir, project);
-        codeGenerator.setResourceBaseDir(resourceBaseDir);
-        LOG.debug("{} Folder: {} marked as resources for generator: {}", LOG_PREFIX, resourceBaseDir,
-                codeGeneratorCfg.getCodeGeneratorClass());
-
-        if (outputDir.exists()) {
-            Files.walk(outputDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
-            LOG.info("{} Succesfully deleted output directory {}", LOG_PREFIX, outputDir);
-        }
-        final Stopwatch watch = Stopwatch.createStarted();
-        Collection<File> generated = codeGenerator.generateSources(context.getContext(), outputDir,
-            context.getYangModules(), context);
-
-        LOG.debug("{} Sources generated by {}: {}", LOG_PREFIX, codeGeneratorCfg.getCodeGeneratorClass(), generated);
-        LOG.info("{} Sources generated by {}: {} in {}", LOG_PREFIX, codeGeneratorCfg.getCodeGeneratorClass(),
-                generated == null ? 0 : generated.size(), watch);
-    }
-
-    /**
-     * Instantiate object from fully qualified class name.
-     */
-    private static <T> T getInstance(final String codeGeneratorClass, final Class<T> baseType)
-            throws ReflectiveOperationException {
-        final Class<?> clazz = Class.forName(codeGeneratorClass);
-        checkArgument(baseType.isAssignableFrom(clazz), "Code generator %s has to implement %s", clazz, baseType);
-        return baseType.cast(clazz.getConstructor().newInstance());
+        return allFiles.build();
     }
 }
index 43faea0b25c232b6ab97a2069afb88697a033852..cf32486272b0db68dc1e805046186a2bac7c8b49 100644 (file)
@@ -7,6 +7,10 @@
  */
 package org.opendaylight.yangtools.yang2sources.plugin;
 
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
 import com.google.common.collect.ImmutableList;
 import java.io.File;
 import java.util.ArrayList;
@@ -14,7 +18,6 @@ import java.util.List;
 import org.apache.maven.model.Build;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.project.MavenProject;
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -39,29 +42,29 @@ public class YangToSourcesMojoTest {
 
     @Test
     public void yangToSourceMojoTest() throws Exception {
-        Mockito.when(this.project.getPlugin(YangToSourcesMojo.PLUGIN_NAME)).thenReturn(this.plugin);
+        doReturn(plugin).when(project).getPlugin(YangToSourcesMojo.PLUGIN_NAME);
 
         this.mojo = new YangToSourcesMojo();
-        this.mojo.setProject(this.project);
+        this.mojo.setProject(project);
         this.mojo.buildContext = new DefaultBuildContext();
         this.mojo.execute();
-        Assert.assertNotNull(this.mojo);
+        assertNotNull(this.mojo);
 
         final YangToSourcesProcessor processor = Mockito.mock(YangToSourcesProcessor.class);
         this.mojo = new YangToSourcesMojo(processor);
-        this.mojo.setProject(this.project);
+        this.mojo.setProject(project);
         this.mojo.execute();
-        Mockito.verify(processor).conditionalExecute(false);
+        verify(processor).conditionalExecute(false);
     }
 
     @Test
     public void test() throws Exception {
         prepareProcessor();
-        Assert.assertNotNull(this.proc);
-        this.mojo = new YangToSourcesMojo(this.proc);
-        this.mojo.setProject(this.project);
+        assertNotNull(proc);
+        this.mojo = new YangToSourcesMojo(proc);
+        this.mojo.setProject(project);
         this.mojo.execute();
-        Assert.assertNotNull(this.mojo);
+        assertNotNull(mojo);
     }
 
     private void prepareProcessor() {
@@ -72,11 +75,11 @@ public class YangToSourcesMojoTest {
         final CodeGeneratorArg codeGeneratorArg = new CodeGeneratorArg(GeneratorMock.class.getName(),
                 "target/YangToSourcesMojoTest-outputBaseDir");
         codeGenerators.add(codeGeneratorArg);
-        final MavenProject mvnProject = Mockito.mock(MavenProject.class);
         final Build build = new Build();
-        Mockito.when(mvnProject.getBuild()).thenReturn(build);
+        build.setDirectory("testDir");
+        doReturn(build).when(project).getBuild();
         final boolean dependencies = true;
         this.proc = new YangToSourcesProcessor(file, ImmutableList.of(excludedYang), codeGenerators,
-                mvnProject, dependencies, YangProvider.getInstance());
+            project, dependencies, YangProvider.getInstance());
     }
 }
index 2250ed4dddb9f6e416f0f4bc2bc62e9c8f7095eb..23b6f01ee518e84c8ff2a1edd48b7e34aa9fdfc4 100644 (file)
@@ -7,56 +7,64 @@
  */
 package org.opendaylight.yangtools.yang2sources.plugin;
 
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.doReturn;
+
 import com.google.common.collect.ImmutableList;
 import java.io.File;
 import java.util.List;
 import org.apache.maven.model.Build;
 import org.apache.maven.project.MavenProject;
-import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
 import org.opendaylight.yangtools.yang2sources.plugin.GenerateSourcesTest.GeneratorMock;
 
-@RunWith(MockitoJUnitRunner.Silent.class)
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class YangToSourcesProcessorTest {
+    @Mock
+    public File buildContext;
+    @Mock
+    public MavenProject project;
+    @Mock
+    public YangProvider inspectDependencies;
 
-    private final File buildContext = Mockito.mock(File.class);
-    private final List<File> yangFilesRootDir = ImmutableList.of(buildContext);
-    private final MavenProject project = Mockito.mock(MavenProject.class);
     private final boolean dep = false;
-    private final YangProvider inspectDependencies = Mockito.mock(YangProvider.class);
+    private List<File> yangFilesRootDir;
+
+    @Before
+    public void before() {
+        yangFilesRootDir = ImmutableList.of(buildContext);
+    }
 
     @Test
     public void yangToSourcesProcessotTest() {
-        Mockito.when(this.buildContext.getPath()).thenReturn("path");
         YangToSourcesProcessor processor = new YangToSourcesProcessor(buildContext, yangFilesRootDir,
             ImmutableList.of(), project, dep, inspectDependencies);
-        Assert.assertNotNull(processor);
+        assertNotNull(processor);
 
         processor = new YangToSourcesProcessor(buildContext, yangFilesRootDir, ImmutableList.of(), project, dep,
             inspectDependencies);
-        Assert.assertNotNull(processor);
+        assertNotNull(processor);
     }
 
     @Test
     public void test() throws Exception {
         final File file = new File(getClass().getResource("/yang").getFile());
         final File excludedYang = new File(getClass().getResource("/yang/excluded-file.yang").getFile());
-        final String path = file.getPath();
         final CodeGeneratorArg codeGeneratorArg = new CodeGeneratorArg(GeneratorMock.class.getName(),
                 "target/YangToSourcesProcessorTest-outputBaseDir");
         final List<CodeGeneratorArg> codeGenerators = ImmutableList.of(codeGeneratorArg);
-        final MavenProject mvnProject = Mockito.mock(MavenProject.class);
         final Build build = new Build();
-        Mockito.when(mvnProject.getBuild()).thenReturn(build);
+        build.setDirectory("foo");
+        doReturn(build).when(project).getBuild();
         final boolean dependencies = true;
         final YangToSourcesProcessor proc = new YangToSourcesProcessor(file, ImmutableList.of(excludedYang),
-            codeGenerators, mvnProject, dependencies, YangProvider.getInstance());
-        Assert.assertNotNull(proc);
+            codeGenerators, project, dependencies, YangProvider.getInstance());
+        assertNotNull(proc);
         proc.execute();
     }
-
 }