Populate yang-parser-api 12/65312/17
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 8 Nov 2017 13:36:20 +0000 (14:36 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 20 Nov 2017 08:37:21 +0000 (09:37 +0100)
This adds a simpistic API to access a pre-configured YANG inference engine,
so its complexities are hidden from users. Both a ServiceLoader and OSGi
loading interface is provided.

Change-Id: Iab444bd1698273c30ac37b8d3ea577ae35db71e7
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
15 files changed:
yang/yang-model-validator/src/main/java/org/opendaylight/yangtools/yang/validator/SystemTestUtils.java
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParser.java [new file with mode: 0644]
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserException.java [new file with mode: 0644]
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserFactory.java [new file with mode: 0644]
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangSyntaxErrorException.java
yang/yang-parser-impl/pom.xml
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserFactoryImpl.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/Activator.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/package-info.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java
yang/yang-parser-impl/src/main/resources/META-INF/services/org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository [deleted file]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/CustomCrossSourceStatementReactorBuilder.java
yang/yang-test-util/src/main/java/org/opendaylight/yangtools/yang/test/util/YangParserTestUtils.java

index e4eba0bdf43cb6230929a9c60261e6f849e667fa..b0280fad76f37a02bdd500bf160e4311d3a24848 100644 (file)
@@ -19,25 +19,34 @@ import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
+import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+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.impl.DefaultReactors;
-import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
-import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
-import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
-import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
 
 final class SystemTestUtils {
 
     private static final Pattern MODULE_PATTERN = Pattern.compile("module(.*?)\\{");
     private static final Pattern WHITESPACES = Pattern.compile("\\s+");
+    private static final @NonNull YangParserFactory PARSER_FACTORY;
+
+    static {
+        final Iterator<@NonNull YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
+        if (!it.hasNext()) {
+            throw new IllegalStateException("No YangParserFactory found");
+        }
+        PARSER_FACTORY = it.next();
+    }
 
     private SystemTestUtils() {
         throw new UnsupportedOperationException();
@@ -49,8 +58,7 @@ final class SystemTestUtils {
     };
 
     static SchemaContext parseYangSources(final List<String> yangLibDirs, final List<String> yangTestFiles,
-            final Set<QName> supportedFeatures, final boolean recursiveSearch)
-            throws ReactorException, IOException, YangSyntaxErrorException {
+            final Set<QName> supportedFeatures, final boolean recursiveSearch) throws IOException, YangParserException {
         /*
          * Current dir "." should be always present implicitly in the list of
          * directories where dependencies are searched for
@@ -77,24 +85,22 @@ final class SystemTestUtils {
     }
 
     static SchemaContext parseYangSources(final Set<QName> supportedFeatures, final List<File> testFiles,
-            final List<File> libFiles) throws ReactorException, IOException, YangSyntaxErrorException {
-        final List<StatementStreamSource> testSources = getYangStatementSources(testFiles);
-        final List<StatementStreamSource> libSources = getYangStatementSources(libFiles);
-        return parseYangSources(testSources, libSources, supportedFeatures);
-    }
-
-    static SchemaContext parseYangSources(final List<StatementStreamSource> testSources,
-            final List<StatementStreamSource> libSources, final Set<QName> supportedFeatures) throws ReactorException {
-        Preconditions.checkArgument(testSources != null && !testSources.isEmpty(), "No yang sources");
-
-        final BuildAction reactor = DefaultReactors.defaultReactor().newBuild()
-                .addLibSources(libSources).addSources(testSources);
+            final List<File> libFiles) throws IOException, YangParserException {
+        Preconditions.checkArgument(!testFiles.isEmpty(), "No yang sources");
 
+        final YangParser parser = PARSER_FACTORY.createParser();
         if (supportedFeatures != null) {
-            reactor.setSupportedFeatures(supportedFeatures);
+            parser.setSupportedFeatures(supportedFeatures);
         }
 
-        return reactor.buildEffective();
+        for (File file : testFiles) {
+            parser.addSource(YangTextSchemaSource.forFile(file));
+        }
+        for (File file : libFiles) {
+            parser.addLibSource(YangTextSchemaSource.forFile(file));
+        }
+
+        return parser.buildSchemaContext();
     }
 
     private static File findInFiles(final List<File> libFiles, final String yangTestFile) throws IOException {
@@ -109,26 +115,13 @@ final class SystemTestUtils {
     private static String getModelNameFromFile(final File file) throws IOException {
         final String fileAsString = readFile(file.getAbsolutePath());
         final Matcher matcher = MODULE_PATTERN.matcher(fileAsString);
-        if (matcher.find()) {
-            return matcher.group(1);
-        } else {
-            return "";
-        }
+        return matcher.find() ? matcher.group(1) : "";
     }
 
     private static String readFile(final String path) throws IOException {
         return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
     }
 
-    private static List<StatementStreamSource> getYangStatementSources(final List<File> yangFiles)
-            throws IOException, YangSyntaxErrorException {
-        final List<StatementStreamSource> yangSources = new ArrayList<>(yangFiles.size());
-        for (final File file : yangFiles) {
-            yangSources.add(YangStatementStreamSource.create(YangTextSchemaSource.forFile(file)));
-        }
-        return yangSources;
-    }
-
     private static Collection<File> getYangFiles(final String yangSourcesDirectoryPath, final boolean recursiveSearch)
             throws FileNotFoundException {
         final File testSourcesDir = new File(yangSourcesDirectoryPath);
diff --git a/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParser.java b/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParser.java
new file mode 100644 (file)
index 0000000..ab287e4
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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.yang.model.parser.api;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+
+/**
+ * Configurable single-use YANG parser. Each instance can be configured to use a different set of models after
+ * which it is built. Models once added cannot be removed.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NotThreadSafe
+public interface YangParser {
+    /**
+     * Return enumeration of concrete types of {@link SchemaSourceRepresentation} parsers created from this factory
+     * support. Users can use this
+     *
+     * @return Enumeration of supported schema source representations.
+     */
+    Collection<Class<? extends SchemaSourceRepresentation>> supportedSourceRepresentations();
+
+    /**
+     * Return the set of all YANG statements semantically supported by this parser instance.
+     *
+     * @return Set of all YANG statements semantically supported by this parser instance.
+     */
+    Set<QName> supportedStatements();
+
+    /**
+     * Add main source. All main sources are present in resulting SchemaContext.
+     *
+     * @param source
+     *            which should be added into main sources
+     * @throws YangSyntaxErrorException when one of the sources fails syntactic analysis
+     * @throws IOException when an IO error occurs
+     * @throws IllegalArgumentException if the representation is not supported
+     */
+    YangParser addSource(final SchemaSourceRepresentation source) throws IOException, YangSyntaxErrorException;
+
+    /**
+     * Add main sources. All main sources are present in resulting SchemaContext.
+     *
+     * @param sources
+     *            which should be added into main sources
+     * @throws YangSyntaxErrorException when one of the sources fails syntactic analysis
+     * @throws IOException when an IO error occurs
+     * @throws IllegalArgumentException if the representation is not supported
+     */
+    default YangParser addSources(final SchemaSourceRepresentation... sources) throws IOException,
+        YangSyntaxErrorException {
+        for (SchemaSourceRepresentation source : sources) {
+            addSource(source);
+        }
+        return this;
+    }
+
+    default YangParser addSources(final Collection<? extends SchemaSourceRepresentation> sources) throws IOException,
+        YangSyntaxErrorException {
+        for (SchemaSourceRepresentation source : sources) {
+            addSource(source);
+        }
+        return this;
+    }
+
+    YangParser addLibSource(SchemaSourceRepresentation source) throws IOException, YangSyntaxErrorException;
+
+    /**
+     * Add library sources. Only library sources required by main sources are present in resulting SchemaContext.
+     * Any other library sources are ignored and this also applies to error reporting.
+     *
+     * <p>
+     * Note: Library sources are not supported in semantic version mode currently.
+     *
+     * @param sources
+     *            YANG sources which should be added into library sources
+     * @throws YangSyntaxErrorException when one of the sources fails syntactic analysis
+     * @throws IOException when an IO error occurs
+     * @throws IllegalArgumentException if the representation is not supported
+     */
+    default YangParser addLibSources(final SchemaSourceRepresentation... sources) throws IOException,
+            YangSyntaxErrorException {
+        for (SchemaSourceRepresentation source : sources) {
+            addLibSource(source);
+        }
+        return this;
+    }
+
+    default YangParser addLibSources(final Collection<SchemaSourceRepresentation> sources) throws IOException,
+            YangSyntaxErrorException {
+        for (SchemaSourceRepresentation source : sources) {
+            addLibSource(source);
+        }
+        return this;
+    }
+
+    /**
+     * Set supported features based on which all if-feature statements in the
+     * parsed YANG modules will be resolved. If this method is not invoked, all features will be supported.
+     *
+     * @param supportedFeatures
+     *            Set of supported features in the final SchemaContext.
+     *            If the set is empty, no features encountered will be supported.
+     */
+    YangParser setSupportedFeatures(@Nonnull final Set<QName> supportedFeatures);
+
+    /**
+     * Set YANG modules which can be deviated by specified modules during the parsing process.
+     * Map key (QNameModule) denotes a module which can be deviated by the modules in the Map value.
+     *
+     * @param modulesDeviatedByModules
+     *            Map of YANG modules (Map key) which can be deviated by specified modules (Map value) in the final
+     *            SchemaContext. If the map is empty, no deviations encountered will be supported.
+     */
+    YangParser setModulesWithSupportedDeviations(
+            @Nonnull Map<QNameModule, Set<QNameModule>> modulesDeviatedByModules);
+
+    /**
+     * Build the declared view of a combined view of declared statements.
+     *
+     * @return Ordered collection of declared statements from requested sources.
+     */
+    List<DeclaredStatement<?>> buildDeclaredModel() throws YangParserException;
+
+    /**
+     * Build effective {@link SchemaContext}
+     *
+     * @return An effective schema context comprised of configured models.
+     * @throws YangSyntaxErrorException When a syntactic error is encountered.
+     */
+    SchemaContext buildSchemaContext() throws YangParserException;
+}
diff --git a/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserException.java b/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserException.java
new file mode 100644 (file)
index 0000000..96e4793
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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.yang.model.parser.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Base exception reported by {@link YangParser}. This is used as an exception of last resort, semantic reporting
+ * of individual errors is performed via subclasses, like {@link YangSyntaxErrorException}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public class YangParserException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public YangParserException(final String message) {
+        super(requireNonNull(message));
+    }
+
+    public YangParserException(final String message, final Throwable cause) {
+        super(requireNonNull(message), cause);
+    }
+}
diff --git a/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserFactory.java b/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserFactory.java
new file mode 100644 (file)
index 0000000..94c3074
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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.yang.model.parser.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+
+/**
+ * Basic entry point into a YANG parser implementation.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangParserFactory {
+    /**
+     * Return enumeration of {@link StatementParserMode}s supported by this factory.
+     *
+     * @return Enumeration of supported schema source representations.
+     */
+    Collection<StatementParserMode> supportedParserModes();
+
+    /**
+     * Create a {@link YangParser} instance operating in default import resolution mode.
+     *
+     * @return A new {@link YangParser} instance
+     */
+    default YangParser createParser() {
+        return createParser(StatementParserMode.DEFAULT_MODE);
+    }
+
+    /**
+     * Create a {@link YangParser} instance operating in specified import resolution mode.
+     *
+     * @param parserMode Requested parser mode, may not be null.
+     * @return A new {@link YangParser} instance
+     * @throws NullPointerException if parser mode is null
+     * @throws IllegalArgumentException if specified parser mode is not supported
+     */
+    YangParser createParser(StatementParserMode parserMode);
+}
index c2da1bdbd7b79e418c4ebe91f9e24627894ef8b1..5731e18eb7de727f7116a83f981db1c53664cbb2 100644 (file)
@@ -7,13 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.model.parser.api;
 
-import static java.util.Objects.requireNonNull;
-
 import java.util.Optional;
 import javax.annotation.Nullable;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
-public class YangSyntaxErrorException extends Exception {
+public class YangSyntaxErrorException extends YangParserException {
     private static final long serialVersionUID = 2L;
 
     private final SourceIdentifier source;
@@ -27,7 +25,7 @@ public class YangSyntaxErrorException extends Exception {
 
     public YangSyntaxErrorException(@Nullable final SourceIdentifier source, final int line,
             final int charPositionInLine, final String message, @Nullable final Throwable cause) {
-        super(requireNonNull(message), cause);
+        super(message, cause);
         this.source = source;
         this.line = line;
         this.charPositionInLine = charPositionInLine;
index 260ef3d901651fcc77a3a24fbf57cdff6a7c9a37..73dd50c0a560fc211a980d68787110f895f70b12 100644 (file)
     </dependencyManagement>
 
     <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>yang-common</artifactId>
             <artifactId>rfc8040-parser-support</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
             <artifactId>logback-classic</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>concepts</artifactId>
-        </dependency>
     </dependencies>
 
     <build>
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
-                        <Export-Package>
-                            {local-packages},
-                            org.opendaylight.yangtools.yang.parser.impl.*,
-                            ;-split-package:=error
-                        </Export-Package>
+                        <Include-Resource>{META-INF/services=${project.build.directory}/classes/META-INF/services}</Include-Resource>
+                        <Bundle-Activator>org.opendaylight.yangtools.yang.parser.impl.osgi.Activator</Bundle-Activator>
                     </instructions>
                 </configuration>
             </plugin>
index 03f56aea985143f71c9e4263feaa0a24ac09c0d1..03fc317681062f6beca463dc1c10c826fe1f9ac1 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang.parser.impl;
 
 import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.odlext.parser.AnyxmlSchemaLocationNamespace;
 import org.opendaylight.yangtools.odlext.parser.AnyxmlSchemaLocationStatementSupport;
 import org.opendaylight.yangtools.odlext.parser.AnyxmlStatementSupportOverride;
@@ -30,7 +31,7 @@ import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementR
  */
 @Beta
 public final class DefaultReactors {
-    private static final CrossSourceStatementReactor DEFAULT_REACTOR = defaultReactorBuilder().build();
+    private static final @NonNull CrossSourceStatementReactor DEFAULT_REACTOR = defaultReactorBuilder().build();
 
     private DefaultReactors() {
         throw new UnsupportedOperationException();
@@ -38,11 +39,18 @@ public final class DefaultReactors {
 
     /**
      * Get a shared default-configured reactor instance. This instance is configured to handle both RFC6020 and RFC7950,
-     * as well as RFC8040's yang-data extension.
+     * as well as
+     * <ul>
+     * <li>RFC6536's default-deny-{all,write} extensions</li>
+     * <li>RFC7952's annotation extension</li>
+     * <li>RFC8040's yang-data extension</li>
+     * <li>OpenConfig extensions</li>
+     * <li>OpenDaylight extensions</li>
+     * </ul>
      *
      * @return a shared default-configured reactor instance.
      */
-    public static CrossSourceStatementReactor defaultReactor() {
+    public static @NonNull CrossSourceStatementReactor defaultReactor() {
         return DEFAULT_REACTOR;
     }
 
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserFactoryImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserFactoryImpl.java
new file mode 100644 (file)
index 0000000..10872fb
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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.yang.parser.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import javax.annotation.concurrent.ThreadSafe;
+import org.eclipse.jdt.annotation.NonNull;
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+/**
+ * Reference {@link YangParserFactory} implementation.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@ThreadSafe
+@MetaInfServices
+public final class YangParserFactoryImpl implements YangParserFactory {
+    private static final Collection<StatementParserMode> SUPPORTED_MODES = ImmutableList.of(
+        StatementParserMode.DEFAULT_MODE, StatementParserMode.SEMVER_MODE);
+
+    private final CrossSourceStatementReactor reactor;
+
+    /**
+     * Construct a new {@link YangParserFactory} backed by {@link DefaultReactors#defaultReactor()}.
+     */
+    public YangParserFactoryImpl() {
+        this(DefaultReactors.defaultReactor());
+    }
+
+    /**
+     * Construct a new {@link YangParserFactory} backed by specified reactor.
+     *
+     * @param reactor Backing reactor
+     */
+    public YangParserFactoryImpl(@NonNull final CrossSourceStatementReactor reactor) {
+        this.reactor = requireNonNull(reactor);
+    }
+
+    @Override
+    public Collection<StatementParserMode> supportedParserModes() {
+        return SUPPORTED_MODES;
+    }
+
+    @Override
+    public YangParser createParser(final StatementParserMode parserMode) {
+        return new YangParserImpl(reactor.newBuild(parserMode));
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java
new file mode 100644 (file)
index 0000000..1774187
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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.yang.parser.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.xml.transform.TransformerException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+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.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinDomSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinXmlSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinTextToDomTransformer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.xml.sax.SAXException;
+
+final class YangParserImpl implements YangParser {
+    private static final Collection<Class<? extends SchemaSourceRepresentation>> REPRESENTATIONS = ImmutableList.of(
+        YangTextSchemaSource.class, YinDomSchemaSource.class, YinXmlSchemaSource.class, YinTextSchemaSource.class);
+
+    private final BuildAction buildAction;
+
+    YangParserImpl(final BuildAction buildAction) {
+        this.buildAction = requireNonNull(buildAction);
+    }
+
+    @Override
+    public Collection<Class<? extends SchemaSourceRepresentation>> supportedSourceRepresentations() {
+        return REPRESENTATIONS;
+    }
+
+    @Override
+    public Set<QName> supportedStatements() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public YangParser addSource(final SchemaSourceRepresentation source) throws IOException, YangSyntaxErrorException {
+        buildAction.addSources(sourceToStatementStream(source));
+        return null;
+    }
+
+    @Override
+    public YangParser addLibSource(final SchemaSourceRepresentation source) throws IOException,
+            YangSyntaxErrorException {
+        buildAction.addLibSources(sourceToStatementStream(source));
+        return null;
+    }
+
+    @Override
+    public YangParser setSupportedFeatures(final Set<QName> supportedFeatures) {
+        buildAction.setSupportedFeatures(supportedFeatures);
+        return this;
+    }
+
+    @Override
+    public YangParser setModulesWithSupportedDeviations(
+            final Map<QNameModule, Set<QNameModule>> modulesDeviatedByModules) {
+        buildAction.setModulesWithSupportedDeviations(modulesDeviatedByModules);
+        return this;
+    }
+
+    @Override
+    public List<DeclaredStatement<?>> buildDeclaredModel() throws YangParserException {
+        try {
+            return buildAction.build().getRootStatements();
+        } catch (ReactorException e) {
+            throw decodeReactorException(e);
+        }
+    }
+
+    @Override
+    public SchemaContext buildSchemaContext() throws YangParserException {
+        try {
+            return buildAction.buildEffective();
+        } catch (ReactorException e) {
+            throw decodeReactorException(e);
+        }
+    }
+
+    private static YangParserException decodeReactorException(final ReactorException reported) {
+        // FIXME: map exception in some reasonable manner
+        return new YangParserException("Failed to assemble sources", reported);
+    }
+
+    private static StatementStreamSource sourceToStatementStream(final SchemaSourceRepresentation source)
+            throws IOException, YangSyntaxErrorException {
+        requireNonNull(source);
+        if (source instanceof YangTextSchemaSource) {
+            return YangStatementStreamSource.create((YangTextSchemaSource) source);
+        } else if (source instanceof YinDomSchemaSource) {
+            return YinStatementStreamSource.create((YinDomSchemaSource) source);
+        } else if (source instanceof YinTextSchemaSource) {
+            try {
+                return YinStatementStreamSource.create(YinTextToDomTransformer.transformSource(
+                    (YinTextSchemaSource) source));
+            } catch (SAXException e) {
+                throw new YangSyntaxErrorException(source.getIdentifier(), 0, 0, "Failed to parse XML text", e);
+            }
+        } else if (source instanceof YinXmlSchemaSource) {
+            try {
+                return YinStatementStreamSource.create((YinXmlSchemaSource) source);
+            } catch (TransformerException e) {
+                throw new YangSyntaxErrorException(source.getIdentifier(), 0, 0,
+                    "Failed to assemble in-memory representation", e);
+            }
+        } else {
+            throw new IllegalArgumentException("Unsupported source " + source);
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/Activator.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/Activator.java
new file mode 100644 (file)
index 0000000..c42d0b3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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.yang.parser.impl.osgi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserFactoryImpl;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * YANG parser implementation activator. Publishes a {@link YangParserFactory} implementation on bundle start.
+ *
+ * @author Robert Varga
+ */
+public final class Activator implements BundleActivator {
+    private ServiceRegistration<@NonNull YangParserFactory> registration;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        registration = context.registerService(YangParserFactory.class, new YangParserFactoryImpl(), null);
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        if (registration != null) {
+            registration.unregister();
+            registration = null;
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/package-info.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/package-info.java
new file mode 100644 (file)
index 0000000..f052302
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, 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
+ */
+/**
+ * OSGi-specific YANG parser code. Contains activators and similar.
+ */
+package org.opendaylight.yangtools.yang.parser.impl.osgi;
\ No newline at end of file
index 6c615b58a9414613e9fc7eac24ed92b13657ac04..3682b1014f212ff386f646d5e2c72ef2195b58c3 100644 (file)
@@ -13,6 +13,7 @@ import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import javax.annotation.Nonnull;
+import org.kohsuke.MetaInfServices;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
@@ -28,6 +29,7 @@ import org.opendaylight.yangtools.yang.model.repo.util.AbstractSchemaRepository;
  * Note: for current implementation, "same" means the same filter and the same set of {@link SourceIdentifier}s.
  */
 @Beta
+@MetaInfServices(value = SchemaRepository.class)
 public final class SharedSchemaRepository extends AbstractSchemaRepository implements Identifiable<String> {
     private final LoadingCache<SchemaSourceFilter, SchemaContextFactory> cache =
             CacheBuilder.newBuilder().softValues().build(new CacheLoader<SchemaSourceFilter, SchemaContextFactory>() {
diff --git a/yang/yang-parser-impl/src/main/resources/META-INF/services/org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository b/yang/yang-parser-impl/src/main/resources/META-INF/services/org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository
deleted file mode 100644 (file)
index 44871af..0000000
+++ /dev/null
@@ -1 +0,0 @@
-org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository
index d2838568a5f8188d277ffaf35b02904057c29940..4f63e37e544df47f6c3f938e26a9170cc3026797 100644 (file)
@@ -16,6 +16,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.concepts.Builder;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.YangVersion;
@@ -109,7 +110,7 @@ public class CustomCrossSourceStatementReactorBuilder implements Builder<CrossSo
     }
 
     @Override
-    public CrossSourceStatementReactor build() {
+    public @NonNull CrossSourceStatementReactor build() {
         final StatementSupportBundle initBundle = reactorSupportBundles.get(ModelProcessingPhase.INIT).build();
         final StatementSupportBundle preLinkageBundle = reactorSupportBundles
                 .get(ModelProcessingPhase.SOURCE_PRE_LINKAGE).setParent(initBundle).build();
index 1931a4a722aadbeb162cea5a86265f7ab381dc24..ed1a0de441bd3537b8cd9e4d7bb6f1b9a58ab0c8 100644 (file)
@@ -17,21 +17,22 @@ import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
+import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 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.YangParserFactory;
 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.parser.impl.DefaultReactors;
-import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
-import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinStatementStreamSource;
-import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
-import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
-import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
 
 /**
  * Utility class which provides convenience methods for producing effective schema context based on the supplied
@@ -45,6 +46,16 @@ public final class YangParserTestUtils {
         return name.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION) && file.isFile();
     };
 
+    private static final @NonNull YangParserFactory PARSER_FACTORY;
+
+    static {
+        final Iterator<@NonNull YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
+        if (!it.hasNext()) {
+            throw new IllegalStateException("No YangParserFactory found");
+        }
+        PARSER_FACTORY = it.next();
+    }
+
     private YangParserTestUtils() {
         throw new UnsupportedOperationException("Utility class should not be instantiated.");
     }
@@ -186,7 +197,7 @@ public final class YangParserTestUtils {
      */
     public static SchemaContext parseYangFiles(final Set<QName> supportedFeatures,
             final StatementParserMode parserMode, final Collection<File> files) {
-        return parseYangSources(supportedFeatures, parserMode,
+        return parseSources(parserMode, supportedFeatures,
             files.stream().map(YangTextSchemaSource::forFile).collect(Collectors.toList()));
     }
 
@@ -264,7 +275,7 @@ public final class YangParserTestUtils {
         for (final String r : resources) {
             sources.add(YangTextSchemaSource.forResource(clazz, r));
         }
-        return parseYangSources(null, StatementParserMode.DEFAULT_MODE, sources);
+        return parseSources(StatementParserMode.DEFAULT_MODE, null, sources);
     }
 
     /**
@@ -326,101 +337,28 @@ public final class YangParserTestUtils {
 
     public static SchemaContext parseYangSources(final StatementParserMode parserMode,
             final Set<QName> supportedFeatures, final YangTextSchemaSource... sources) {
-        return parseYangSources(supportedFeatures, parserMode, Arrays.asList(sources));
+        return parseSources(parserMode, supportedFeatures, Arrays.asList(sources));
     }
 
-    public static SchemaContext parseYangSources(final Set<QName> supportedFeatures,
-            final StatementParserMode parserMode, final Collection<? extends YangTextSchemaSource> sources) {
-        final Collection<YangStatementStreamSource> streams = new ArrayList<>(sources.size());
-        for (YangTextSchemaSource source : sources) {
-            try {
-                streams.add(YangStatementStreamSource.create(source));
-            } catch (IOException e) {
-                throw new IllegalArgumentException("Failed to read source " + source, e);
-            } catch (YangSyntaxErrorException e) {
-                throw new IllegalArgumentException("Malformed source " + source, e);
-            }
+    public static SchemaContext parseSources(final StatementParserMode parserMode, final Set<QName> supportedFeatures,
+            final Collection<? extends SchemaSourceRepresentation> sources) {
+        final YangParser parser = PARSER_FACTORY.createParser(parserMode);
+        if (supportedFeatures != null) {
+            parser.setSupportedFeatures(supportedFeatures);
         }
 
-        return parseSources(supportedFeatures, parserMode, streams);
-    }
-
-    /**
-     * Creates a new effective schema context containing the specified YIN sources. Statement parser mode is set to
-     * default mode and all YANG features are supported.
-     *
-     * @param sources YIN sources to be parsed
-     * @return effective schema context
-     */
-    public static SchemaContext parseYinSources(final YinStatementStreamSource... sources) {
-        return parseYinSources(StatementParserMode.DEFAULT_MODE, sources);
-    }
-
-    /**
-     * Creates a new effective schema context containing the specified YIN sources. Statement parser mode is set to
-     * default mode.
-     *
-     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YIN
-     *                          models are resolved
-     * @param sources YIN sources to be parsed
-     *
-     * @return effective schema context
-     * @throws ReactorException if there is an error in one of the parsed YIN sources
-     */
-    public static SchemaContext parseYinSources(final Set<QName> supportedFeatures,
-            final YinStatementStreamSource... sources) throws ReactorException {
-        return parseSources(supportedFeatures, StatementParserMode.DEFAULT_MODE, sources);
-    }
-
-    /**
-     * Creates a new effective schema context containing the specified YIN sources. All YANG features are supported.
-     *
-     * @param statementParserMode mode of statement parser
-     * @param sources YIN sources to be parsed
-     * @return effective schema context
-     */
-    public static SchemaContext parseYinSources(final StatementParserMode statementParserMode,
-            final YinStatementStreamSource... sources) {
-        return parseSources(null, statementParserMode, sources);
-    }
-
-    /**
-     * Creates a new effective schema context containing the specified YANG sources.
-     *
-     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
-     *                          models are resolved
-     * @param parserMode mode of statement parser
-     * @param sources sources to be parsed
-     *
-     * @return effective schema context
-     */
-    public static SchemaContext parseSources(final Set<QName> supportedFeatures,
-            final StatementParserMode parserMode, final StatementStreamSource... sources) {
-        return parseSources(supportedFeatures, parserMode, Arrays.asList(sources));
-    }
-
-    /**
-     * Creates a new effective schema context containing the specified YANG sources.
-     *
-     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
-     *                          models are resolved
-     * @param statementParserMode mode of statement parser
-     * @param sources sources to be parsed
-     *
-     * @return effective schema context
-     */
-    public static SchemaContext parseSources(final Set<QName> supportedFeatures,
-            final StatementParserMode statementParserMode, final Collection<? extends StatementStreamSource> sources) {
-        final BuildAction reactor = DefaultReactors.defaultReactor().newBuild(statementParserMode);
-        if (supportedFeatures != null) {
-            reactor.setSupportedFeatures(supportedFeatures);
+        try {
+            parser.addSources(sources);
+        } catch (YangSyntaxErrorException e) {
+            throw new IllegalArgumentException("Malformed source", e);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed to read a source", e);
         }
-        reactor.addSources(sources);
 
         try {
-            return reactor.buildEffective();
-        } catch (ReactorException e) {
-            throw new IllegalStateException(e);
+            return parser.buildSchemaContext();
+        } catch (YangParserException e) {
+            throw new IllegalStateException("Failed to assemble SchemaContext", e);
         }
     }