Merge PatternStatementSupport
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / repo / YangModelDependencyInfo.java
index d06e15ea0612d06eefb52d04b4b15f458df46ffe..0e380901eecd1ad4cea877f93c902bb1f5b5753d 100644 (file)
@@ -10,31 +10,32 @@ package org.opendaylight.yangtools.yang.parser.rfc7950.repo;
 import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.annotations.VisibleForTesting;
+import com.google.common.annotations.Beta;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import java.io.IOException;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import org.antlr.v4.runtime.ParserRuleContext;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.ArgumentContext;
-import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
 import org.opendaylight.yangtools.concepts.SemVer;
 import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
-import org.opendaylight.yangtools.yang.common.YangVersion;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRArgument;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword.Unqualified;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRStatement;
 import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 
@@ -63,6 +64,7 @@ public abstract class YangModelDependencyInfo {
 
     private static final String OPENCONFIG_VERSION = OpenConfigStatements.OPENCONFIG_VERSION.getStatementName()
             .getLocalName();
+    @Deprecated
     private static final Splitter COLON_SPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
 
     private final String name;
@@ -164,31 +166,34 @@ public abstract class YangModelDependencyInfo {
     }
 
     /**
-     * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of a YANG model.
+     * Extracts {@link YangModelDependencyInfo} from an intermediate representation root statement of a YANG model.
      *
-     * @param source Source identifier
-     * @param tree Abstract syntax tree
+     * @param source Schema source
      * @return {@link YangModelDependencyInfo}
-     * @throws YangSyntaxErrorException If the AST is not a valid YANG module/submodule
+     * @throws IllegalArgumentException If the root statement is not a valid YANG module/submodule
      */
-    static @NonNull YangModelDependencyInfo fromAST(final SourceIdentifier source, final ParserRuleContext tree)
-            throws YangSyntaxErrorException {
-
-        if (tree instanceof StatementContext) {
-            final StatementContext rootStatement = (StatementContext) tree;
-            return parseAST(rootStatement, source);
-        }
-
-        throw new YangSyntaxErrorException(source, 0, 0, "Unknown YANG text type");
+    public static @NonNull YangModelDependencyInfo forIR(final IRSchemaSource source) {
+        return forIR(source.getRootStatement(), source.getIdentifier());
     }
 
-    private static @NonNull YangModelDependencyInfo parseAST(final StatementContext rootStatement,
+    /**
+     * Extracts {@link YangModelDependencyInfo} from an intermediate representation root statement of a YANG model.
+     *
+     * @param source Source identifier
+     * @param rootStatement root statement
+     * @return {@link YangModelDependencyInfo}
+     * @throws IllegalArgumentException If the root statement is not a valid YANG module/submodule
+     */
+    static @NonNull YangModelDependencyInfo forIR(final IRStatement rootStatement,
             final SourceIdentifier source) {
-        final String keyWordText = rootStatement.keyword().getText();
-        if (MODULE.equals(keyWordText)) {
+        final IRKeyword keyword = rootStatement.keyword();
+        checkArgument(keyword instanceof Unqualified, "Invalid root statement %s", keyword);
+
+        final String arg = keyword.identifier();
+        if (MODULE.equals(arg)) {
             return parseModuleContext(rootStatement, source);
         }
-        if (SUBMODULE.equals(keyWordText)) {
+        if (SUBMODULE.equals(arg)) {
             return parseSubmoduleContext(rootStatement, source);
         }
         throw new IllegalArgumentException("Root of parsed AST must be either module or submodule");
@@ -205,18 +210,33 @@ public abstract class YangModelDependencyInfo {
      * @throws IOException When the resource cannot be read
      * @throws IllegalArgumentException
      *             If input stream is not valid YANG stream
+     * @deprecated This method was used by testing framework and was deemed to be potentially useful to the outside
+     *             world. With Java Platform Module System, though, the resource loading rules have changed to the point
+     *             where we no longer can guarantee it working correctly, as the results depend on the resource path.
+     *             Users are advised to use {@link #forYangText(YangTextSchemaSource)}.
      */
-    @VisibleForTesting
+    @Deprecated(forRemoval = true)
     public static YangModelDependencyInfo forResource(final Class<?> refClass, final String resourceName)
             throws IOException, YangSyntaxErrorException {
-        final YangStatementStreamSource source = YangStatementStreamSource.create(
-            YangTextSchemaSource.forResource(refClass, resourceName));
-        final ParserRuleContext ast = source.getYangAST();
-        checkArgument(ast instanceof StatementContext);
-        return parseAST((StatementContext) ast, source.getIdentifier());
+        return forYangText(YangTextSchemaSource.forResource(refClass, resourceName));
     }
 
-    private static @NonNull YangModelDependencyInfo parseModuleContext(final StatementContext module,
+    /**
+     * Extracts {@link YangModelDependencyInfo} from a {@link YangTextSchemaSource}. This parsing does not
+     * validate full YANG module, only parses header up to the revisions and imports.
+     *
+     * @param yangText {@link YangTextSchemaSource}
+     * @return {@link YangModelDependencyInfo}
+     * @throws YangSyntaxErrorException If the resource does not pass syntactic analysis
+     * @throws IOException When the resource cannot be read
+     */
+    public static YangModelDependencyInfo forYangText(final YangTextSchemaSource yangText)
+            throws IOException, YangSyntaxErrorException {
+        final YangStatementStreamSource source = YangStatementStreamSource.create(yangText);
+        return forIR(source.rootStatement(), source.getIdentifier());
+    }
+
+    private static @NonNull YangModelDependencyInfo parseModuleContext(final IRStatement module,
             final SourceIdentifier source) {
         final String name = safeStringArgument(source, module, "module name");
         final String latestRevision = getLatestRevision(module, source);
@@ -227,28 +247,28 @@ public abstract class YangModelDependencyInfo {
         return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
     }
 
-    private static ImmutableSet<ModuleImport> parseImports(final StatementContext module,
+    private static ImmutableSet<ModuleImport> parseImports(final IRStatement module,
             final SourceIdentifier source) {
         final Set<ModuleImport> result = new HashSet<>();
-        for (final StatementContext subStatementContext : module.statement()) {
-            if (IMPORT.equals(subStatementContext.keyword().getText())) {
-                final String importedModuleName = safeStringArgument(source, subStatementContext,
-                    "imported module name");
-                final String revisionDateStr = getRevisionDateString(subStatementContext, source);
+        for (final IRStatement substatement : module.statements()) {
+            if (isBuiltin(substatement, IMPORT)) {
+                final String importedModuleName = safeStringArgument(source, substatement, "imported module name");
+                final String revisionDateStr = getRevisionDateString(substatement, source);
                 final Revision revisionDate = Revision.ofNullable(revisionDateStr).orElse(null);
-                final SemVer importSemVer = findSemanticVersion(subStatementContext, source);
+                final SemVer importSemVer = findSemanticVersion(substatement, source);
                 result.add(new ModuleImportImpl(importedModuleName, revisionDate, importSemVer));
             }
         }
         return ImmutableSet.copyOf(result);
     }
 
-    private static SemVer findSemanticVersion(final StatementContext statement, final SourceIdentifier source) {
+    @Beta
+    public static SemVer findSemanticVersion(final IRStatement statement, final SourceIdentifier source) {
         String semVerString = null;
-        for (final StatementContext subStatement : statement.statement()) {
-            final String subStatementName = trimPrefix(subStatement.keyword().getText());
-            if (OPENCONFIG_VERSION.equals(subStatementName)) {
-                semVerString = safeStringArgument(source,  subStatement, "version string");
+        for (final IRStatement substatement : statement.statements()) {
+            // FIXME: this should also check we are using a prefix
+            if (OPENCONFIG_VERSION.equals(substatement.keyword().identifier())) {
+                semVerString = safeStringArgument(source,  substatement, "version string");
                 break;
             }
         }
@@ -256,24 +276,17 @@ public abstract class YangModelDependencyInfo {
         return Strings.isNullOrEmpty(semVerString) ? null : SemVer.valueOf(semVerString);
     }
 
-
-    private static String trimPrefix(final String identifier) {
-        final List<String> namesParts = COLON_SPLITTER.splitToList(identifier);
-        if (namesParts.size() == 2) {
-            return namesParts.get(1);
-        }
-        return identifier;
+    private static boolean isBuiltin(final IRStatement stmt, final String localName) {
+        final IRKeyword keyword = stmt.keyword();
+        return keyword instanceof Unqualified && localName.equals(keyword.identifier());
     }
 
-
-    private static ImmutableSet<ModuleImport> parseIncludes(final StatementContext module,
-            final SourceIdentifier source) {
+    private static ImmutableSet<ModuleImport> parseIncludes(final IRStatement module, final SourceIdentifier source) {
         final Set<ModuleImport> result = new HashSet<>();
-        for (final StatementContext subStatementContext : module.statement()) {
-            if (INCLUDE.equals(subStatementContext.keyword().getText())) {
-                final String revisionDateStr = getRevisionDateString(subStatementContext, source);
-                final String IncludeModuleName = safeStringArgument(source, subStatementContext,
-                    "included submodule name");
+        for (final IRStatement substatement : module.statements()) {
+            if (isBuiltin(substatement, INCLUDE)) {
+                final String revisionDateStr = getRevisionDateString(substatement, source);
+                final String IncludeModuleName = safeStringArgument(source, substatement, "included submodule name");
                 final Revision revisionDate = Revision.ofNullable(revisionDateStr).orElse(null);
                 result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
             }
@@ -281,21 +294,21 @@ public abstract class YangModelDependencyInfo {
         return ImmutableSet.copyOf(result);
     }
 
-    private static String getRevisionDateString(final StatementContext importStatement, final SourceIdentifier source) {
+    private static String getRevisionDateString(final IRStatement importStatement, final SourceIdentifier source) {
         String revisionDateStr = null;
-        for (final StatementContext importSubStatement : importStatement.statement()) {
-            if (REVISION_DATE.equals(importSubStatement.keyword().getText())) {
-                revisionDateStr = safeStringArgument(source, importSubStatement, "imported module revision-date");
+        for (final IRStatement substatement : importStatement.statements()) {
+            if (isBuiltin(substatement, REVISION_DATE)) {
+                revisionDateStr = safeStringArgument(source, substatement, "imported module revision-date");
             }
         }
         return revisionDateStr;
     }
 
-    public static String getLatestRevision(final StatementContext module, final SourceIdentifier source) {
+    public static String getLatestRevision(final IRStatement module, final SourceIdentifier source) {
         String latestRevision = null;
-        for (final StatementContext subStatementContext : module.statement()) {
-            if (REVISION.equals(subStatementContext.keyword().getText())) {
-                final String currentRevision = safeStringArgument(source, subStatementContext, "revision date");
+        for (final IRStatement substatement : module.statements()) {
+            if (isBuiltin(substatement, REVISION)) {
+                final String currentRevision = safeStringArgument(source, substatement, "revision date");
                 if (latestRevision == null || latestRevision.compareTo(currentRevision) < 0) {
                     latestRevision = currentRevision;
                 }
@@ -304,7 +317,7 @@ public abstract class YangModelDependencyInfo {
         return latestRevision;
     }
 
-    private static @NonNull YangModelDependencyInfo parseSubmoduleContext(final StatementContext submodule,
+    private static @NonNull YangModelDependencyInfo parseSubmoduleContext(final IRStatement submodule,
             final SourceIdentifier source) {
         final String name = safeStringArgument(source, submodule, "submodule name");
         final String belongsTo = parseBelongsTo(submodule, source);
@@ -316,27 +329,25 @@ public abstract class YangModelDependencyInfo {
         return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
     }
 
-    private static String parseBelongsTo(final StatementContext submodule, final SourceIdentifier source) {
-        for (final StatementContext subStatementContext : submodule.statement()) {
-            if (BELONGS_TO.equals(subStatementContext.keyword().getText())) {
-                return safeStringArgument(source, subStatementContext, "belongs-to module name");
+    private static String parseBelongsTo(final IRStatement submodule, final SourceIdentifier source) {
+        for (final IRStatement substatement : submodule.statements()) {
+            if (isBuiltin(substatement, BELONGS_TO)) {
+                return safeStringArgument(source, substatement, "belongs-to module name");
             }
         }
         return null;
     }
 
-    private static String safeStringArgument(final SourceIdentifier source, final StatementContext stmt,
-            final String desc) {
+    static String safeStringArgument(final SourceIdentifier source, final IRStatement stmt, final String desc) {
         final StatementSourceReference ref = getReference(source, stmt);
-        final ArgumentContext arg = stmt.argument();
+        final IRArgument arg = stmt.argument();
         checkArgument(arg != null, "Missing %s at %s", desc, ref);
-        return ArgumentContextUtils.stringFromStringContext(arg, YangVersion.VERSION_1, ref);
+        // TODO: we probably need to understand yang version first....
+        return ArgumentContextUtils.rfc6020().stringFromStringContext(arg, ref);
     }
 
-    private static StatementSourceReference getReference(final SourceIdentifier source,
-            final StatementContext context) {
-        return DeclarationInTextSource.atPosition(source.getName(), context.getStart().getLine(),
-            context.getStart().getCharPositionInLine());
+    private static StatementSourceReference getReference(final SourceIdentifier source, final IRStatement stmt) {
+        return DeclarationInTextSource.atPosition(source.getName(), stmt.startLine(), stmt.startColumn());
     }
 
     /**
@@ -387,6 +398,7 @@ public abstract class YangModelDependencyInfo {
     /**
      * Utility implementation of {@link ModuleImport} to be used by {@link YangModelDependencyInfo}.
      */
+    // FIXME: this is a rather nasty misuse of APIs :(
     private static final class ModuleImportImpl implements ModuleImport {
 
         private final Revision revision;
@@ -434,6 +446,11 @@ public abstract class YangModelDependencyInfo {
             return Optional.empty();
         }
 
+        @Override
+        public ImportEffectiveStatement asEffectiveStatement() {
+            throw new UnsupportedOperationException();
+        }
+
         @Override
         public int hashCode() {
             final int prime = 31;