Switch YangToSourcesProcessor to use YangParser 16/67816/4
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 1 Feb 2018 12:39:17 +0000 (13:39 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 2 Feb 2018 02:35:08 +0000 (03:35 +0100)
Rather than going through YangTextSchemaContextResolver, use
YangParser to assemble the SchemaContext. This gives us better
control over the parsing process and also allows us to build
only sources which are required to generate code in local directory.

JIRA: YANGTOOLS-825
Change-Id: Ie3c49517aedd5ae3604ed27dc5bd1f1f1fa7a4d1
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-maven-plugin-it/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/it/YangToSourcesPluginTestIT.java
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ProcessorModuleReactor.java
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java

index 6fdb88ac18d4934f3675adcb781e57e06774333b..14dd7171c5d6e10cc9394c1c6376112fc039489e 100644 (file)
@@ -62,10 +62,7 @@ public class YangToSourcesPluginTestIT {
             setUp("test-parent/MissingYangInDep/", false);
             fail("Verification exception should have been thrown");
         } catch (VerificationException e) {
-            assertVerificationException(e,
-                    "SchemaResolutionException{unsatisfiedImports={RevisionSourceIdentifier"
-                    + " [name=private@2013-02-27]=[ModuleImportImpl"
-                    + " [name=unknownDep, revision=2013-02-27, semanticVersion=null]]}");
+            assertVerificationException(e, "Imported module [unknownDep] was not found.");
             return;
         }
     }
index f801fb074452e1032f4bb02d3e0b6366ccd61de6..75bb7e15e27a0b6c7b3a3c250e47165b902bd931 100644 (file)
@@ -10,21 +10,20 @@ package org.opendaylight.yangtools.yang2sources.plugin;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Verify;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 import javax.annotation.concurrent.NotThreadSafe;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,48 +36,51 @@ import org.slf4j.LoggerFactory;
 final class ProcessorModuleReactor {
     private static final Logger LOG = LoggerFactory.getLogger(ProcessorModuleReactor.class);
 
-    private final YangTextSchemaContextResolver resolver;
-    private final Set<SourceIdentifier> sourcesInProject;
+    private final Map<SourceIdentifier, YangTextSchemaSource> modelsInProject;
+    private final YangParser parser;
 
-    ProcessorModuleReactor(final YangTextSchemaContextResolver resolver) {
-        this.resolver = Preconditions.checkNotNull(resolver);
-        sourcesInProject = ImmutableSet.copyOf(resolver.getAvailableSources());
+    ProcessorModuleReactor(final YangParser parser, final Collection<YangTextSchemaSource> modelsInProject) {
+        this.parser = Preconditions.checkNotNull(parser);
+        this.modelsInProject = Maps.uniqueIndex(modelsInProject, YangTextSchemaSource::getIdentifier);
     }
 
-    void registerSource(final YangTextSchemaSource source) throws SchemaSourceException, IOException,
-            YangSyntaxErrorException {
-        resolver.registerSource(source);
+    void registerSourceFromDependency(final YangTextSchemaSource source) throws YangSyntaxErrorException, IOException {
+        // This source is coming from a dependency:
+        // - its identifier should be accurate, as it should have been processed into a file with accurate name
+        // - it is not required to be parsed, hence we add it just as a library source
+        parser.addLibSource(source);
     }
 
-    ContextHolder toContext() throws SchemaResolutionException {
-        final SchemaContext schemaContext = Verify.verifyNotNull(resolver.trySchemaContext());
+    ContextHolder toContext() throws YangParserException {
+        final SchemaContext schemaContext = Verify.verifyNotNull(parser.buildSchemaContext());
 
         final Set<Module> modules = new HashSet<>();
         for (Module module : schemaContext.getModules()) {
             final SourceIdentifier modId = Util.moduleToIdentifier(module);
             LOG.debug("Looking for source {}", modId);
-            if (sourcesInProject.contains(modId)) {
+            if (modelsInProject.containsKey(modId)) {
                 LOG.debug("Module {} belongs to current project", module);
                 modules.add(module);
 
                 for (Module sub : module.getSubmodules()) {
                     final SourceIdentifier subId = Util.moduleToIdentifier(sub);
-                    if (sourcesInProject.contains(subId)) {
+                    if (modelsInProject.containsKey(subId)) {
                         LOG.warn("Submodule {} not found in input files", sub);
                     }
                 }
             }
         }
 
-        return new ContextHolder(schemaContext, modules, sourcesInProject);
+        return new ContextHolder(schemaContext, modules, modelsInProject.keySet());
     }
 
     Collection<YangTextSchemaSource> getModelsInProject() {
-        return Collections2.transform(sourcesInProject, id -> resolver.getSourceTexts(id).iterator().next());
+        return modelsInProject.values();
     }
 
     @Override
     public String toString() {
-        return MoreObjects.toStringHelper(this).add("sources", sourcesInProject).add("resolver", resolver).toString();
+        return MoreObjects.toStringHelper(this).add("sources", modelsInProject.keySet()).add("parser", parser)
+                .toString();
     }
 }
index d8c0e0402a2bc8e8d283c1c545df21fc75311043..d53d06ee18cda46a2deeacf02c873782c8fc8cf0 100644 (file)
@@ -7,10 +7,11 @@
  */
 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;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -25,19 +26,23 @@ 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;
 import java.util.Optional;
+import java.util.ServiceLoader;
 import java.util.Set;
 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.yang.common.YangConstants;
-import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.parser.repo.YangTextSchemaContextResolver;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
 import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
 import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
 import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
@@ -49,6 +54,13 @@ import org.sonatype.plexus.build.incremental.DefaultBuildContext;
 
 class YangToSourcesProcessor {
     private static final Logger LOG = LoggerFactory.getLogger(YangToSourcesProcessor.class);
+    private static final YangParserFactory DEFAULT_PARSER_FACTORY;
+
+    static {
+        final Iterator<YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
+        checkState(it.hasNext(), "Failed to find a YangParserFactory implementation");
+        DEFAULT_PARSER_FACTORY = it.next();
+    }
 
     static final String LOG_PREFIX = "yang-to-sources:";
     private static final String META_INF_STR = "META-INF";
@@ -58,6 +70,7 @@ class YangToSourcesProcessor {
     static final String META_INF_YANG_STRING_JAR = META_INF_STR + "/" + YANG_STR;
     static final String META_INF_YANG_SERVICES_STRING_JAR = META_INF_STR + "/" + "services";
 
+    private final YangParserFactory parserFactory;
     private final File yangFilesRootDir;
     private final Set<File> excludedFiles;
     private final List<CodeGeneratorArg> codeGenerators;
@@ -76,6 +89,7 @@ class YangToSourcesProcessor {
         this.project = requireNonNull(project);
         this.inspectDependencies = inspectDependencies;
         this.yangProvider = requireNonNull(yangProvider);
+        this.parserFactory = DEFAULT_PARSER_FACTORY;
     }
 
     @VisibleForTesting
@@ -108,7 +122,7 @@ class YangToSourcesProcessor {
 
             try {
                 holder = createContextHolder(reactor);
-            } catch (SchemaSourceException | YangSyntaxErrorException e) {
+            } catch (YangParserException e) {
                 throw new MojoFailureException("Failed to process reactor " + reactor, e);
             } catch (IOException e) {
                 throw new MojoExecutionException("Failed to read reactor " + reactor, e);
@@ -164,15 +178,28 @@ class YangToSourcesProcessor {
                 return Optional.empty();
             }
 
-            final YangTextSchemaContextResolver resolver = YangTextSchemaContextResolver.create("maven-plugin");
+            // FIXME: add correct mode
+            final YangParser parser = parserFactory.createParser();
+            final List<YangTextSchemaSource> sourcesInProject = new ArrayList<>(yangFilesInProject.size());
             for (final File f : yangFilesInProject) {
-                resolver.registerSource(YangTextSchemaSource.forFile(f));
+                final YangTextSchemaSource textSource = YangTextSchemaSource.forFile(f);
+                final ASTSchemaSource astSource = TextToASTTransformer.transformText(textSource);
+
+                parser.addSource(astSource);
+
+                if (!astSource.getIdentifier().equals(textSource.getIdentifier())) {
+                    // AST indicates a different source identifier, make sure we use that
+                    sourcesInProject.add(YangTextSchemaSource.delegateForByteSource(astSource.getIdentifier(),
+                        textSource));
+                } else {
+                    sourcesInProject.add(textSource);
+                }
             }
 
             LOG.debug("Processed project files: {}", yangFilesInProject);
             LOG.info("{} Project model files parsed: {}", LOG_PREFIX, yangFilesInProject.size());
 
-            final ProcessorModuleReactor reactor = new ProcessorModuleReactor(resolver);
+            final ProcessorModuleReactor reactor = new ProcessorModuleReactor(parser, sourcesInProject);
             LOG.debug("Initialized reactor {}", reactor, yangFilesInProject);
             return Optional.of(reactor);
         } catch (Exception e) {
@@ -185,7 +212,7 @@ class YangToSourcesProcessor {
     }
 
     private ContextHolder createContextHolder(final ProcessorModuleReactor reactor) throws MojoFailureException,
-            IOException, SchemaSourceException, YangSyntaxErrorException {
+            IOException, YangParserException {
         /**
          * Set contains all modules generated from input sources. Number of
          * modules may differ from number of sources due to submodules
@@ -196,7 +223,7 @@ class YangToSourcesProcessor {
             final List<YangTextSchemaSource> sourcesInDependencies = Util.findYangFilesInDependenciesAsStream(
                 project);
             for (YangTextSchemaSource s : toUniqueSources(sourcesInDependencies)) {
-                reactor.registerSource(s);
+                reactor.registerSourceFromDependency(s);
             }
         }
 
@@ -236,7 +263,7 @@ class YangToSourcesProcessor {
      */
     @SuppressWarnings("checkstyle:illegalCatch")
     private void generateSources(final ContextHolder context) throws MojoFailureException {
-        if (codeGenerators.size() == 0) {
+        if (codeGenerators.isEmpty()) {
             LOG.warn("{} No code generators provided", LOG_PREFIX);
             return;
         }
@@ -311,9 +338,7 @@ class YangToSourcesProcessor {
     private static <T> T getInstance(final String codeGeneratorClass, final Class<T> baseType) throws
             ClassNotFoundException, InstantiationException, IllegalAccessException {
         final Class<?> clazz = Class.forName(codeGeneratorClass);
-
-        Preconditions.checkArgument(baseType.isAssignableFrom(clazz), "Code generator %s has to implement %s", clazz,
-            baseType);
+        checkArgument(baseType.isAssignableFrom(clazz), "Code generator %s has to implement %s", clazz, baseType);
         return baseType.cast(clazz.newInstance());
     }
 }