Yang-maven-plugin refactored and config attributes and spi adjusted.
[controller.git] / opendaylight / sal / yang-prototype / code-generator / maven-yang-plugin / src / main / java / org / opendaylight / controller / yang2sources / plugin / YangToSourcesMojo.java
index f8117c077ed807a0f5ceafdad138e7dc5a808bb0..6e43b306cd44d82b1cdcaa32b5b54c976fc41a71 100644 (file)
@@ -7,26 +7,11 @@
  */
 package org.opendaylight.controller.yang2sources.plugin;
 
-import java.io.Closeable;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Arrays;
 import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 
-import org.apache.commons.io.IOUtils;
-import org.apache.maven.model.Resource;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
@@ -35,19 +20,11 @@ import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.project.MavenProject;
-import org.opendaylight.controller.yang.model.api.Module;
 import org.opendaylight.controller.yang.model.api.SchemaContext;
-import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
-import org.opendaylight.controller.yang.parser.impl.YangParserImpl;
 import org.opendaylight.controller.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
-import org.opendaylight.controller.yang2sources.plugin.ConfigArg.ResourceProviderArg;
 import org.opendaylight.controller.yang2sources.spi.CodeGenerator;
-import org.opendaylight.controller.yang2sources.spi.ResourceGenerator;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.io.Files;
 
 /**
  * Generate sources from yang files using user provided set of
@@ -64,9 +41,6 @@ import com.google.common.io.Files;
  */
 @Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true)
 public final class YangToSourcesMojo extends AbstractMojo {
-    private static final String LOG_PREFIX = "yang-to-sources:";
-    private static final String INPUT_RESOURCE_DIR = "META-INF/yang/";
-    private static final String OUTPUT_RESOURCE_DIR = "/target/external-resources/";
 
     /**
      * Classes implementing {@link CodeGenerator} interface. An instance will be
@@ -74,397 +48,74 @@ public final class YangToSourcesMojo extends AbstractMojo {
      * CodeGenerator#generateSources(SchemaContext, File, Set<String>
      * yangModulesNames)} will be called on every instance.
      */
-    @Parameter(required = true)
+    @Parameter(required = false)
     private CodeGeneratorArg[] codeGenerators;
 
     /**
      * Source directory that will be recursively searched for yang files (ending
      * with .yang suffix).
      */
-    @Parameter(required = true)
-    private String yangFilesRootDir;
-
-    /**
-     * Classes implementing {@link ResourceGenerator} interface. An instance
-     * will be created out of every class using default constructor. Method
-     * {@link ResourceGenerator#generateResourceFiles(Collection, File)} will be
-     * called on every instance.
-     */
-    @Parameter(required = true)
-    private ResourceProviderArg[] resourceProviders;
+    @Parameter(required = false)
+    private String yangFilesRootDir; // defaults to ${basedir}/src/main/yang
 
     @Parameter(property = "project", required = true, readonly = true)
     protected MavenProject project;
 
-    private transient final YangModelParser parser;
+    @Parameter(property = "inspectDependencies", required = true, readonly = true)
+    private boolean inspectDependencies;
 
-    @VisibleForTesting
-    YangToSourcesMojo(ResourceProviderArg[] resourceProviderArgs,
-            CodeGeneratorArg[] codeGeneratorArgs, YangModelParser parser,
-            String yangFilesRootDir) {
-        super();
-        this.resourceProviders = resourceProviderArgs;
-        this.codeGenerators = codeGeneratorArgs;
-        this.yangFilesRootDir = yangFilesRootDir;
-        this.parser = parser;
-    }
+    private YangToSourcesProcessor yangToSourcesProcessor;
 
     public YangToSourcesMojo() {
-        super();
-        parser = new YangParserImpl();
-    }
-
-    @Override
-    public void execute() throws MojoExecutionException, MojoFailureException {
-        ContextHolder context = processYang();
-        generateSources(context);
-        generateResources();
-
-        closeResources();
-    }
-
-    /**
-     * Generate {@link SchemaContext} with {@link YangModelParserImpl}
-     */
-    private ContextHolder processYang() throws MojoExecutionException {
-        try {
-            List<InputStream> yangFiles = Util
-                    .listFilesAsStream(yangFilesRootDir);
-            Set<Module> yangModules = parser
-                    .parseYangModelsFromStreams(yangFiles);
-
-            List<InputStream> yangFilesFromDependencies = getFilesFromDependenciesAsStream();
-            Set<Module> yangModulesFromDependencies = parser
-                    .parseYangModelsFromStreams(yangFilesFromDependencies);
-
-            Set<Module> parsedYang = new HashSet<Module>(yangModules);
-            parsedYang.addAll(yangModulesFromDependencies);
-
-            if (yangFiles.isEmpty() && yangFilesFromDependencies.isEmpty()) {
-                getLog().warn(
-                        Util.message(
-                                "No %s file found in %s or in dependencies",
-                                LOG_PREFIX, Util.YANG_SUFFIX, yangFilesRootDir));
-                Set<Module> modules = Collections.emptySet();
-                return new ContextHolder(null, modules);
-            }
-
-            SchemaContext resolveSchemaContext = parser
-                    .resolveSchemaContext(parsedYang);
-            getLog().info(
-                    Util.message("%s files parsed from %s", LOG_PREFIX,
-                            Util.YANG_SUFFIX, yangFiles));
-            return new ContextHolder(resolveSchemaContext, yangModules);
-
-            // MojoExecutionException is thrown since execution cannot continue
-        } catch (Exception e) {
-            String message = Util.message("Unable to parse %s files from %s",
-                    LOG_PREFIX, Util.YANG_SUFFIX, yangFilesRootDir);
-            getLog().error(message, e);
-            throw new MojoExecutionException(message, e);
-        }
-    }
-
-    private void generateResources() throws MojoExecutionException,
-            MojoFailureException {
-        if (resourceProviders.length == 0) {
-            getLog().warn(
-                    Util.message("No resource provider classes provided",
-                            LOG_PREFIX));
-            return;
-        }
-
-        Resource res = new Resource();
-        String baseDirName = project.getBasedir().getAbsolutePath();
-        res.setDirectory(baseDirName + OUTPUT_RESOURCE_DIR);
-        res.setTargetPath(INPUT_RESOURCE_DIR);
-        project.addResource(res);
-
-        Map<String, String> thrown = Maps.newHashMap();
-
-        Collection<File> yangFiles = new ArrayList<File>();
-
-        // load files from yang root
-        yangFiles.addAll(getFilesFromYangRoot());
-
-        // load files from dependencies
-        Collection<File> filesFromDependencies = getFilesFromDependencies();
-        yangFiles.addAll(filesFromDependencies);
-
-        for (ResourceProviderArg resourceProvider : resourceProviders) {
-            try {
-                provideResourcesWithOneProvider(yangFiles, resourceProvider);
-            } catch (Exception e) {
-                // try other generators, exception will be thrown after
-                getLog().error(
-                        Util.message(
-                                "Unable to provide resources with %s resource provider",
-                                LOG_PREFIX,
-                                resourceProvider.getResourceProviderClass()), e);
-                thrown.put(resourceProvider.getResourceProviderClass(), e
-                        .getClass().getCanonicalName());
-            }
-        }
-
-        if (!thrown.isEmpty()) {
-            String message = Util
-                    .message(
-                            "One or more code resource provider failed, including failed list(resourceProviderClass=exception) %s",
-                            LOG_PREFIX, thrown.toString());
-            getLog().error(message);
-            throw new MojoFailureException(message);
-        }
-    }
-
-    private Collection<File> getFilesFromYangRoot() {
-        Collection<File> yangFilesLoaded = null;
-
-        File rootDir = new File(yangFilesRootDir);
-        try {
-            if (rootDir.isAbsolute()) {
-                yangFilesLoaded = Util.listFiles(yangFilesRootDir);
-            } else {
-                String path = project.getBasedir().getAbsolutePath()
-                        + File.separator + yangFilesRootDir;
-                yangFilesLoaded = Util.listFiles(path);
-            }
-        } catch (FileNotFoundException e) {
-            getLog().warn(
-                    "yangFilesRootDir[" + rootDir.getAbsolutePath()
-                            + "] does not exists.");
-            yangFilesLoaded = new ArrayList<File>();
-        }
-
-        Collection<File> yangFiles = new ArrayList<File>(yangFilesLoaded);
-
-        try {
-            for (File yangFile : yangFilesLoaded) {
-                InputStream is = new FileInputStream(yangFile);
-                yangFiles.add(createFileFromStream(is,
-                        project.getBasedir().getAbsolutePath()
-                                + OUTPUT_RESOURCE_DIR + yangFile.getName()));
-                resources.add(is);
-            }
-        } catch (IOException e) {
-            getLog().warn("Exception while loading yang files.", e);
-        }
-        return yangFiles;
-    }
-
-    private Collection<File> getFilesFromDependencies() {
-        Collection<File> yangFiles = new ArrayList<File>();
-
-        try {
-            List<File> filesOnCp = Util.getClassPath(project);
-            List<String> filter = Lists.newArrayList(".yang");
-            for (File file : filesOnCp) {
-                ZipFile zip = new ZipFile(file);
-                Enumeration<? extends ZipEntry> entries = zip.entries();
-
-                while (entries.hasMoreElements()) {
-                    ZipEntry entry = entries.nextElement();
-                    String entryName = entry.getName();
-                    if (entryName.startsWith(INPUT_RESOURCE_DIR)) {
-                        if (entry.isDirectory()) {
-                            continue;
-                        }
-                        if (!Util.acceptedFilter(entryName, filter)) {
-                            continue;
-                        }
-                        InputStream entryStream = zip.getInputStream(entry);
-                        String newEntryName = entryName
-                                .substring(INPUT_RESOURCE_DIR.length());
-                        File tmp = Files.createTempDir();
-                        File f = createFileFromStream(entryStream,
-                                tmp.getAbsolutePath() + newEntryName);
-                        yangFiles.add(f);
-
-                        resources.add(entryStream);
-                    }
-                }
 
-                resources.add(zip);
-            }
-        } catch (Exception e) {
-            getLog().warn("Exception while loading external yang files.", e);
-        }
-        return yangFiles;
-    }
-
-    private File createFileFromStream(InputStream is, String absoluteName)
-            throws IOException {
-        File f = new File(absoluteName);
-        if (!f.exists()) {
-            f.getParentFile().mkdirs();
-        }
-        f.createNewFile();
-
-        FileOutputStream fos = new FileOutputStream(f);
-        IOUtils.copy(is, fos);
-        return f;
-    }
-
-    /**
-     * Instantiate provider from class and call required method
-     */
-    private void provideResourcesWithOneProvider(Collection<File> yangFiles,
-            ResourceProviderArg resourceProvider)
-            throws ClassNotFoundException, InstantiationException,
-            IllegalAccessException {
-
-        resourceProvider.check();
-
-        ResourceGenerator g = Util.getInstance(
-                resourceProvider.getResourceProviderClass(),
-                ResourceGenerator.class);
-        getLog().info(
-                Util.message("Resource provider instantiated from %s",
-                        LOG_PREFIX, resourceProvider.getResourceProviderClass()));
-
-        g.generateResourceFiles(yangFiles, resourceProvider.getOutputBaseDir());
-        getLog().info(
-                Util.message("Resource provider %s call successful",
-                        LOG_PREFIX, resourceProvider.getResourceProviderClass()));
     }
 
-    /**
-     * Call generate on every generator from plugin configuration
-     */
-    private void generateSources(ContextHolder context)
-            throws MojoFailureException {
-        if (codeGenerators.length == 0) {
-            getLog().warn(
-                    Util.message("No code generators provided", LOG_PREFIX));
-            return;
-        }
-
-        Map<String, String> thrown = Maps.newHashMap();
-        for (CodeGeneratorArg codeGenerator : codeGenerators) {
-            try {
-                generateSourcesWithOneGenerator(context, codeGenerator);
-            } catch (Exception e) {
-                // try other generators, exception will be thrown after
-                getLog().error(
-                        Util.message(
-                                "Unable to generate sources with %s generator",
-                                LOG_PREFIX,
-                                codeGenerator.getCodeGeneratorClass()), e);
-                thrown.put(codeGenerator.getCodeGeneratorClass(), e.getClass()
-                        .getCanonicalName());
-            }
-        }
-
-        if (!thrown.isEmpty()) {
-            String message = Util
-                    .message(
-                            "One or more code generators failed, including failed list(generatorClass=exception) %s",
-                            LOG_PREFIX, thrown.toString());
-            getLog().error(message);
-            throw new MojoFailureException(message);
-        }
+    @VisibleForTesting
+    YangToSourcesMojo(YangToSourcesProcessor processor) {
+        this.yangToSourcesProcessor = processor;
     }
 
-    /**
-     * Instantiate generator from class and call required method
-     */
-    private void generateSourcesWithOneGenerator(ContextHolder context,
-            CodeGeneratorArg codeGeneratorCfg) throws ClassNotFoundException,
-            InstantiationException, IllegalAccessException, IOException {
-
-        codeGeneratorCfg.check();
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        if (yangToSourcesProcessor == null) {
+            List<CodeGeneratorArg> codeGeneratorArgs = processCodeGenerators(codeGenerators);
 
-        CodeGenerator g = Util.getInstance(
-                codeGeneratorCfg.getCodeGeneratorClass(), CodeGenerator.class);
-        getLog().info(
-                Util.message("Code generator instantiated from %s", LOG_PREFIX,
-                        codeGeneratorCfg.getCodeGeneratorClass()));
+            // defaults to ${basedir}/src/main/yang
+            File yangFilesRootFile = processYangFilesRootDir(yangFilesRootDir,
+                    project.getBasedir());
 
-        File outputDir = codeGeneratorCfg.getOutputBaseDir();
-        if (project != null && outputDir != null) {
-            project.addCompileSourceRoot(outputDir.getPath());
+            yangToSourcesProcessor = new YangToSourcesProcessor(getLog(),
+                    yangFilesRootFile, codeGeneratorArgs, project,
+                    inspectDependencies);
         }
-        Collection<File> generated = g.generateSources(context.getContext(),
-                outputDir, context.getYangModules());
-        getLog().info(
-                Util.message("Sources generated by %s: %s", LOG_PREFIX,
-                        codeGeneratorCfg.getCodeGeneratorClass(), generated));
+        yangToSourcesProcessor.execute();
     }
 
-    /**
-     * Collection of resources which should be closed after use.
-     */
-    private final List<Closeable> resources = new ArrayList<Closeable>();
-
-    /**
-     * Search for yang files in dependent projects.
-     *
-     * @return files found as List of InputStream
-     */
-    private List<InputStream> getFilesFromDependenciesAsStream() {
-        final List<InputStream> yangsFromDependencies = new ArrayList<InputStream>();
-        try {
-            List<File> filesOnCp = Util.getClassPath(project);
-
-            List<String> filter = Lists.newArrayList(".yang");
-            for (File file : filesOnCp) {
-                ZipFile zip = new ZipFile(file);
-                Enumeration<? extends ZipEntry> entries = zip.entries();
-                while (entries.hasMoreElements()) {
-                    ZipEntry entry = entries.nextElement();
-                    String entryName = entry.getName();
-
-                    if (entryName.startsWith(INPUT_RESOURCE_DIR)) {
-                        if (entry.isDirectory()) {
-                            continue;
-                        }
-                        if (!Util.acceptedFilter(entryName, filter)) {
-                            continue;
-                        }
-
-                        InputStream entryStream = zip.getInputStream(entry);
-                        yangsFromDependencies.add(entryStream);
-                        resources.add(entryStream);
-                    }
-
-                }
-                resources.add(zip);
-            }
-        } catch (Exception e) {
-            getLog().warn("Exception while searching yangs in dependencies", e);
+    private static List<CodeGeneratorArg> processCodeGenerators(
+            CodeGeneratorArg[] codeGenerators) {
+        List<CodeGeneratorArg> codeGeneratorArgs;
+        if (codeGenerators == null) {
+            codeGeneratorArgs = Collections.emptyList();
+        } else {
+            codeGeneratorArgs = Arrays.asList(codeGenerators);
         }
-        return yangsFromDependencies;
+        return codeGeneratorArgs;
     }
 
-    /**
-     * Internal utility method for closing open resources.
-     */
-    private void closeResources() {
-        for (Closeable resource : resources) {
-            try {
-                resource.close();
-            } catch (IOException e) {
-                getLog().warn("Failed to close resources: " + resource, e);
+    private static File processYangFilesRootDir(String yangFilesRootDir,
+            File baseDir) {
+        File yangFilesRootFile;
+        if (yangFilesRootDir == null) {
+            yangFilesRootFile = new File(baseDir, "src" + File.separator
+                    + "main" + File.separator + "yang");
+        } else {
+            File file = new File(yangFilesRootDir);
+            if (file.isAbsolute()) {
+                yangFilesRootFile = file;
+            } else {
+                yangFilesRootFile = new File(baseDir, file.getPath());
             }
         }
+        return yangFilesRootFile;
     }
-
-    private class ContextHolder {
-        private final SchemaContext context;
-        private final Set<Module> yangModules;
-
-        private ContextHolder(SchemaContext context,
-                Set<Module> yangModules) {
-            this.context = context;
-            this.yangModules = yangModules;
-        }
-
-        public SchemaContext getContext() {
-            return context;
-        }
-
-        public Set<Module> getYangModules() {
-            return yangModules;
-        }
-    }
-
 }