Fixed build problems with yang-maven-plugin.
[yangtools.git] / yang-maven-plugin / src / main / java / org / opendaylight / yangtools / yang2sources / plugin / YangToSourcesProcessor.java
diff --git a/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java b/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java
new file mode 100644 (file)
index 0000000..ca972bf
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.FileUtils;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.yangtools.yang2sources.plugin.Util.ContextHolder;
+import org.opendaylight.yangtools.yang2sources.plugin.Util.YangsInZipsResult;
+import org.opendaylight.yangtools.yang2sources.spi.CodeGenerator;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+
+class YangToSourcesProcessor {
+    static final String LOG_PREFIX = "yang-to-sources:";
+    static final String META_INF_YANG_STRING = "META-INF" + File.separator
+            + "yang";
+    static final String META_INF_YANG_STRING_JAR = "META-INF" + "/" + "yang";
+    static final File META_INF_YANG_DIR = new File(META_INF_YANG_STRING);
+
+    private final Log log;
+    private final File yangFilesRootDir;
+    private final List<CodeGeneratorArg> codeGenerators;
+    private final MavenProject project;
+    private final boolean inspectDependencies;
+    private YangProvider yangProvider;
+
+    @VisibleForTesting
+    YangToSourcesProcessor(Log log, File yangFilesRootDir,
+                           List<CodeGeneratorArg> codeGenerators, MavenProject project,
+                           boolean inspectDependencies, YangProvider yangProvider) {
+        this.log = Util.checkNotNull(log, "log");
+        this.yangFilesRootDir = Util.checkNotNull(yangFilesRootDir,
+                "yangFilesRootDir");
+        this.codeGenerators = Collections.unmodifiableList(Util.checkNotNull(
+                codeGenerators, "codeGenerators"));
+        this.project = Util.checkNotNull(project, "project");
+        this.inspectDependencies = inspectDependencies;
+        this.yangProvider = yangProvider;
+    }
+
+    YangToSourcesProcessor(Log log, File yangFilesRootDir,
+                           List<CodeGeneratorArg> codeGenerators, MavenProject project,
+                           boolean inspectDependencies) {
+        this(log, yangFilesRootDir, codeGenerators, project,
+                inspectDependencies, new YangProvider());
+    }
+
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        ContextHolder context = processYang();
+        generateSources(context);
+        yangProvider.addYangsToMETA_INF(log, project, yangFilesRootDir);
+    }
+
+    private ContextHolder processYang() throws MojoExecutionException {
+        YangParserImpl parser = new YangParserImpl();
+        List<Closeable> closeables = new ArrayList<>();
+        log.info(Util.message("Inspecting %s", LOG_PREFIX, yangFilesRootDir));
+        try {
+            List<InputStream> yangsInProject = Util
+                    .listFilesAsStream(yangFilesRootDir);
+            List<InputStream> all = new ArrayList<>(yangsInProject);
+            closeables.addAll(yangsInProject);
+            Map<InputStream, Module> allYangModules;
+            Set<Module> projectYangModules;
+            try {
+                if (inspectDependencies) {
+                    YangsInZipsResult dependentYangResult = Util
+                            .findYangFilesInDependenciesAsStream(log, project);
+                    Closeable dependentYangResult1 = dependentYangResult;
+                    closeables.add(dependentYangResult1);
+                    all.addAll(dependentYangResult.yangStreams);
+                }
+
+                allYangModules = parser.parseYangModelsFromStreamsMapped(all);
+
+                projectYangModules = new HashSet<>();
+                for (InputStream inProject : yangsInProject) {
+                    projectYangModules.add(allYangModules.get(inProject));
+                }
+
+            } finally {
+                for (AutoCloseable closeable : closeables) {
+                    closeable.close();
+                }
+            }
+
+            Set<Module> parsedAllYangModules = new HashSet<>(
+                    allYangModules.values());
+            SchemaContext resolveSchemaContext = parser
+                    .resolveSchemaContext(parsedAllYangModules);
+            log.info(Util.message("%s files parsed from %s", LOG_PREFIX,
+                    Util.YANG_SUFFIX.toUpperCase(), yangsInProject));
+            return new ContextHolder(resolveSchemaContext, projectYangModules);
+
+            // 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);
+            log.error(message, e);
+            throw new MojoExecutionException(message, e);
+        }
+    }
+
+    static class YangProvider {
+
+        private static final String yangResourceDir = "target" + File.separator
+                + "yang";
+
+        void addYangsToMETA_INF(Log log, MavenProject project,
+                                File yangFilesRootDir) throws MojoFailureException {
+            File targetYangDir = new File(project.getBasedir(), yangResourceDir);
+
+            try {
+                FileUtils.copyDirectory(yangFilesRootDir, targetYangDir);
+            } catch (IOException e) {
+                String message = "Unable to copy yang files into resource folder";
+                log.warn(message, e);
+                throw new MojoFailureException(message, e);
+            }
+
+            setResource(targetYangDir, META_INF_YANG_STRING_JAR, project);
+
+            log.debug(Util.message(
+                    "Yang files from: %s marked as resources: %s", LOG_PREFIX,
+                    yangFilesRootDir, META_INF_YANG_STRING_JAR));
+        }
+
+        private static void setResource(File targetYangDir, String targetPath,
+                                        MavenProject project) {
+            Resource res = new Resource();
+            res.setDirectory(targetYangDir.getPath());
+            if (targetPath != null)
+                res.setTargetPath(targetPath);
+            project.addResource(res);
+        }
+    }
+
+    /**
+     * Call generate on every generator from plugin configuration
+     */
+    private void generateSources(ContextHolder context)
+            throws MojoFailureException {
+        if (codeGenerators.size() == 0) {
+            log.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
+                log.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());
+            log.error(message);
+            throw new MojoFailureException(message);
+        }
+    }
+
+    /**
+     * Instantiate generator from class and call required method
+     */
+    private void generateSourcesWithOneGenerator(ContextHolder context,
+                                                 CodeGeneratorArg codeGeneratorCfg) throws ClassNotFoundException,
+            InstantiationException, IllegalAccessException, IOException {
+
+        codeGeneratorCfg.check();
+
+        CodeGenerator g = Util.getInstance(
+                codeGeneratorCfg.getCodeGeneratorClass(), CodeGenerator.class);
+        log.info(Util.message("Code generator instantiated from %s",
+                LOG_PREFIX, codeGeneratorCfg.getCodeGeneratorClass()));
+
+        File outputDir = codeGeneratorCfg.getOutputBaseDir(project);
+
+        log.info(Util.message("Sources will be generated to %s", LOG_PREFIX,
+                outputDir));
+        log.debug(Util.message("Project root dir is %s", LOG_PREFIX,
+                project.getBasedir()));
+        log.debug(Util.message(
+                "Additional configuration picked up for : %s: %s", LOG_PREFIX,
+                codeGeneratorCfg.getCodeGeneratorClass(),
+                codeGeneratorCfg.getAdditionalConfiguration()));
+
+        if(outputDir != null) {
+            project.addCompileSourceRoot(outputDir.getAbsolutePath());
+        }
+        g.setLog(log);
+        g.setMavenProject(project);
+        g.setAdditionalConfig(codeGeneratorCfg.getAdditionalConfiguration());
+        File resourceBaseDir = codeGeneratorCfg.getResourceBaseDir(project);
+
+        YangProvider.setResource(resourceBaseDir, null, project);
+        g.setResourceBaseDir(resourceBaseDir);
+        log.debug(Util.message(
+                "Folder: %s marked as resources for generator: %s", LOG_PREFIX,
+                resourceBaseDir, codeGeneratorCfg.getCodeGeneratorClass()));
+
+        Collection<File> generated = g.generateSources(context.getContext(),
+                outputDir, context.getYangModules());
+
+        log.info(Util.message("Sources generated by %s: %s", LOG_PREFIX,
+                codeGeneratorCfg.getCodeGeneratorClass(), generated));
+    }
+
+}