Clean up YangModelDependencyInfo
[yangtools.git] / parser / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / repo / YangModelDependencyInfo.java
index 40b1116fd82e039b8eaa1f09a0dffd39b7325229..c1d35a7b2cbd498aed5699a5bd8799b4b1eb2196 100644 (file)
@@ -7,37 +7,37 @@
  */
 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.Beta;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableSet;
 import java.io.IOException;
-import java.util.HashSet;
+import java.util.Collection;
+import java.util.Comparator;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-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.UnresolvedQName.Unqualified;
+import org.opendaylight.yangtools.yang.common.XMLNamespace;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.ir.IRKeyword;
+import org.opendaylight.yangtools.yang.ir.IRStatement;
+import org.opendaylight.yangtools.yang.ir.YangIRSchemaSource;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSourceReference;
+import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.ImportEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.spi.source.ModuleSourceInfo;
+import org.opendaylight.yangtools.yang.model.spi.source.SourceInfo;
+import org.opendaylight.yangtools.yang.model.spi.source.SourceInfo.Import;
+import org.opendaylight.yangtools.yang.model.spi.source.SourceInfo.Include;
+import org.opendaylight.yangtools.yang.model.spi.source.SubmoduleSourceInfo;
+import org.opendaylight.yangtools.yang.model.spi.source.YangTextSource;
 import org.opendaylight.yangtools.yang.parser.api.YangSyntaxErrorException;
-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.ExplicitStatement;
-import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 
 /**
  * Helper transfer object which holds basic and dependency information for YANG
@@ -53,54 +53,63 @@ import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReferenc
  * @see ModuleDependencyInfo
  * @see SubmoduleDependencyInfo
  */
-public abstract class YangModelDependencyInfo {
+public abstract sealed class YangModelDependencyInfo {
+    /**
+     * Dependency information for a YANG module.
+     */
+    public static final class ModuleDependencyInfo extends YangModelDependencyInfo {
+        private ModuleDependencyInfo(final String name, final Revision revision,
+                final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+            super(name, revision, imports, includes);
+        }
+    }
+
+    /**
+     * Dependency information for a YANG submodule, also provides name for parent module.
+     */
+    public static final class SubmoduleDependencyInfo extends YangModelDependencyInfo {
+        private final @NonNull Unqualified belongsTo;
+
+        private SubmoduleDependencyInfo(final String name, final Revision revision, final Unqualified belongsTo,
+                final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+            super(name, revision, imports, includes);
+            this.belongsTo = requireNonNull(belongsTo);
+        }
+
+        /**
+         * Returns name of parent module.
+         *
+         * @return The module this info belongs to
+         */
+        public @NonNull Unqualified getParentModule() {
+            return belongsTo;
+        }
+    }
+
     private static final String BELONGS_TO = YangStmtMapping.BELONGS_TO.getStatementName().getLocalName();
     private static final String IMPORT = YangStmtMapping.IMPORT.getStatementName().getLocalName();
     private static final String INCLUDE = YangStmtMapping.INCLUDE.getStatementName().getLocalName();
     private static final String MODULE = YangStmtMapping.MODULE.getStatementName().getLocalName();
+    private static final String NAMESPACE = YangStmtMapping.NAMESPACE.getStatementName().getLocalName();
+    private static final String PREFIX = YangStmtMapping.PREFIX.getStatementName().getLocalName();
     private static final String REVISION = YangStmtMapping.REVISION.getStatementName().getLocalName();
     private static final String REVISION_DATE = YangStmtMapping.REVISION_DATE.getStatementName().getLocalName();
     private static final String SUBMODULE = YangStmtMapping.SUBMODULE.getStatementName().getLocalName();
+    private static final String YANG_VERSION = YangStmtMapping.YANG_VERSION.getStatementName().getLocalName();
 
-    private static final String OPENCONFIG_VERSION = OpenConfigStatements.OPENCONFIG_VERSION.getStatementName()
-            .getLocalName();
-    @Deprecated
-    private static final Splitter COLON_SPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
+    private final @NonNull String name;
+    private final @Nullable Revision revision;
+    private final @NonNull ImmutableSet<ModuleImport> submoduleIncludes;
+    private final @NonNull ImmutableSet<ModuleImport> moduleImports;
+    private final @NonNull ImmutableSet<ModuleImport> dependencies;
 
-    private final String name;
-    private final Revision revision;
-    private final SemVer semVer;
-    private final ImmutableSet<ModuleImport> submoduleIncludes;
-    private final ImmutableSet<ModuleImport> moduleImports;
-    private final ImmutableSet<ModuleImport> dependencies;
-
-    YangModelDependencyInfo(final String name, final String formattedRevision,
-            final ImmutableSet<ModuleImport> imports,
+    YangModelDependencyInfo(final String name, final Revision revision, final ImmutableSet<ModuleImport> imports,
             final ImmutableSet<ModuleImport> includes) {
-        this(name, formattedRevision, imports, includes, Optional.empty());
-    }
-
-    YangModelDependencyInfo(final String name, final String formattedRevision,
-            final ImmutableSet<ModuleImport> imports,
-            final ImmutableSet<ModuleImport> includes,
-            final Optional<SemVer> semVer) {
-        this.name = name;
-        this.revision = Revision.ofNullable(formattedRevision).orElse(null);
-        this.moduleImports = imports;
-        this.submoduleIncludes = includes;
-        this.dependencies = ImmutableSet.<ModuleImport>builder()
-                .addAll(moduleImports).addAll(submoduleIncludes).build();
-        this.semVer = semVer.orElse(null);
-    }
-
-    /**
-     * Returns immutable collection of all module imports. This collection contains both <code>import</code> statements
-     * and <code>include</code> statements for submodules.
-     *
-     * @return Immutable collection of imports.
-     */
-    public ImmutableSet<ModuleImport> getDependencies() {
-        return dependencies;
+        this.name = requireNonNull(name);
+        this.revision = revision;
+        moduleImports = requireNonNull(imports);
+        submoduleIncludes = requireNonNull(includes);
+        dependencies = ImmutableSet.<ModuleImport>builder().addAll(moduleImports).addAll(submoduleIncludes).build();
     }
 
     /**
@@ -108,17 +117,18 @@ public abstract class YangModelDependencyInfo {
      *
      * @return model name
      */
-    public String getName() {
+    public final String getName() {
         return name;
     }
 
     /**
      * Returns formatted revision string.
      *
-     * @return formatted revision string
+     * @return formatted revision string, or {@code null}
      */
-    public String getFormattedRevision() {
-        return revision != null ? revision.toString() : null;
+    public final @Nullable String getFormattedRevision() {
+        final var local = revision;
+        return local != null ? local.toString() : null;
     }
 
     /**
@@ -126,43 +136,38 @@ public abstract class YangModelDependencyInfo {
      *
      * @return revision, potentially null
      */
-    public Optional<Revision> getRevision() {
+    public final Optional<Revision> getRevision() {
         return Optional.ofNullable(revision);
     }
 
     /**
-     * Returns semantic version of module.
+     * Returns immutable collection of all module imports. This collection contains both <code>import</code> statements
+     * and <code>include</code> statements for submodules.
      *
-     * @return semantic version
+     * @return Immutable collection of imports.
      */
-    public Optional<SemVer> getSemanticVersion() {
-        return Optional.ofNullable(semVer);
+    public final ImmutableSet<ModuleImport> getDependencies() {
+        return dependencies;
     }
 
     @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + Objects.hashCode(name);
-        result = prime * result + Objects.hashCode(revision);
-        result = prime * result + Objects.hashCode(semVer);
-        return result;
+    public final int hashCode() {
+        return Objects.hash(name, revision);
     }
 
     @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (!(obj instanceof YangModelDependencyInfo)) {
-            return false;
-        }
-        final YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
-        return Objects.equals(name, other.name) && Objects.equals(revision, other.revision)
-                && Objects.equals(semVer, other.semVer);
+    public final boolean equals(final Object obj) {
+        return this == obj || obj instanceof YangModelDependencyInfo other
+            && Objects.equals(name, other.name) && Objects.equals(revision, other.revision);
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues()
+            .add("name", getName())
+            .add("revision", getRevision())
+            .add("dependencies", getDependencies())
+            .toString();
     }
 
     /**
@@ -172,136 +177,159 @@ public abstract class YangModelDependencyInfo {
      * @return {@link YangModelDependencyInfo}
      * @throws IllegalArgumentException If the root statement is not a valid YANG module/submodule
      */
-    public static @NonNull YangModelDependencyInfo forIR(final IRSchemaSource source) {
-        return forIR(source.getRootStatement(), source.getIdentifier());
+    public static @NonNull YangModelDependencyInfo forIR(final YangIRSchemaSource source) {
+        return forIR(source.getRootStatement(), source.sourceId());
     }
 
     /**
      * Extracts {@link YangModelDependencyInfo} from an intermediate representation root statement of a YANG model.
      *
-     * @param source Source identifier
+     * @param sourceId 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 IRKeyword keyword = rootStatement.keyword();
-        checkArgument(keyword instanceof Unqualified, "Invalid root statement %s", keyword);
+            final SourceIdentifier sourceId) {
+        final var keyword = rootStatement.keyword();
+        if (!(keyword instanceof IRKeyword.Unqualified)) {
+            throw new IllegalArgumentException("Invalid root statement " + keyword);
+        }
 
         final String arg = keyword.identifier();
         if (MODULE.equals(arg)) {
-            return parseModuleContext(rootStatement, source);
+            return forSourceInfo(moduleForIR(rootStatement, sourceId));
         }
         if (SUBMODULE.equals(arg)) {
-            return parseSubmoduleContext(rootStatement, source);
+            return forSourceInfo(submmoduleForIR(rootStatement, sourceId));
         }
         throw new IllegalArgumentException("Root of parsed AST must be either module or submodule");
     }
 
-    /**
-     * Extracts {@link YangModelDependencyInfo} from input stream containing a YANG model. This parsing does not
-     * validate full YANG module, only parses header up to the revisions and imports.
-     *
-     * @param refClass Base search class
-     * @param resourceName resource name, relative to refClass
-     * @return {@link YangModelDependencyInfo}
-     * @throws YangSyntaxErrorException If the resource does not pass syntactic analysis
-     * @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)}.
-     */
-    @Deprecated(forRemoval = true)
-    public static YangModelDependencyInfo forResource(final Class<?> refClass, final String resourceName)
-            throws IOException, YangSyntaxErrorException {
-        return forYangText(YangTextSchemaSource.forResource(refClass, resourceName));
+    public static @NonNull YangModelDependencyInfo forSourceInfo(final SourceInfo info) {
+        if (info instanceof ModuleSourceInfo module) {
+            return forSourceInfo(module);
+        } else if (info instanceof SubmoduleSourceInfo submodule) {
+            return forSourceInfo(submodule);
+        } else {
+            throw new IllegalArgumentException("Unhandled source info " + requireNonNull(info));
+        }
+    }
+
+    public static @NonNull ModuleDependencyInfo forSourceInfo(final @NonNull ModuleSourceInfo info) {
+        return new ModuleDependencyInfo(info.name().getLocalName(), latestRevision(info.revisions()),
+            info.imports().stream().map(ModuleImportImpl::new).collect(ImmutableSet.toImmutableSet()),
+            info.includes().stream().map(ModuleImportImpl::new).collect(ImmutableSet.toImmutableSet()));
+    }
+
+    public static @NonNull SubmoduleDependencyInfo forSourceInfo(final @NonNull SubmoduleSourceInfo info) {
+        return new SubmoduleDependencyInfo(info.name().getLocalName(), latestRevision(info.revisions()),
+            info.belongsTo(), info.imports().stream().map(ModuleImportImpl::new).collect(ImmutableSet.toImmutableSet()),
+            info.includes().stream().map(ModuleImportImpl::new).collect(ImmutableSet.toImmutableSet()));
     }
 
     /**
-     * Extracts {@link YangModelDependencyInfo} from a {@link YangTextSchemaSource}. This parsing does not
-     * validate full YANG module, only parses header up to the revisions and imports.
+     * Extracts {@link YangModelDependencyInfo} from a {@link YangTextSource}. This parsing does not validate full YANG
+     * module, only parses header up to the revisions and imports.
      *
-     * @param yangText {@link YangTextSchemaSource}
+     * @param yangText {@link YangTextSource}
      * @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)
+    public static YangModelDependencyInfo forYangText(final YangTextSource yangText)
             throws IOException, YangSyntaxErrorException {
-        final YangStatementStreamSource source = YangStatementStreamSource.create(yangText);
+        final var 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);
-        final Optional<SemVer> semVer = Optional.ofNullable(findSemanticVersion(module, source));
-        final ImmutableSet<ModuleImport> imports = parseImports(module, source);
-        final ImmutableSet<ModuleImport> includes = parseIncludes(module, source);
+    private static @NonNull ModuleSourceInfo moduleForIR(final IRStatement root, final SourceIdentifier sourceId) {
+        return new ModuleSourceInfo(Unqualified.of(safeStringArgument(sourceId, root, "module name")),
+            extractYangVersion(root, sourceId), extractNamespace(root, sourceId), extractPrefix(root, sourceId),
+            extractRevisions(root, sourceId), extractImports(root, sourceId), extractIncludes(root, sourceId));
+    }
 
-        return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
+    private static @NonNull SubmoduleSourceInfo submmoduleForIR(final IRStatement root,
+            final SourceIdentifier sourceId) {
+        return new SubmoduleSourceInfo(Unqualified.of(safeStringArgument(sourceId, root, "submodule name")),
+            extractYangVersion(root, sourceId), extractBelongsTo(root, sourceId),
+            extractRevisions(root, sourceId), extractImports(root, sourceId), extractIncludes(root, sourceId));
     }
 
-    private static ImmutableSet<ModuleImport> parseImports(final IRStatement module,
-            final SourceIdentifier source) {
-        final Set<ModuleImport> result = new HashSet<>();
-        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(substatement, source);
-                result.add(new ModuleImportImpl(importedModuleName, revisionDate, importSemVer));
-            }
-        }
-        return ImmutableSet.copyOf(result);
+    private static @Nullable Revision latestRevision(final Collection<Revision> revision) {
+        return revision.stream().sorted(Comparator.reverseOrder()).findFirst().orElse(null);
     }
 
-    @Beta
-    public static SemVer findSemanticVersion(final IRStatement statement, final SourceIdentifier source) {
-        String semVerString = null;
-        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;
-            }
-        }
+    private static YangVersion extractYangVersion(final IRStatement root, final SourceIdentifier sourceId) {
+        return root.statements().stream()
+            .filter(stmt -> isBuiltin(stmt, YANG_VERSION))
+            .findFirst()
+            .map(stmt -> safeStringArgument(sourceId, stmt, "yang-version argument"))
+            .map(YangVersion::forString)
+            .orElse(YangVersion.VERSION_1);
+    }
 
-        return Strings.isNullOrEmpty(semVerString) ? null : SemVer.valueOf(semVerString);
+    private static @NonNull XMLNamespace extractNamespace(final IRStatement root, final SourceIdentifier sourceId) {
+        return root.statements().stream()
+            .filter(stmt -> isBuiltin(stmt, NAMESPACE))
+            .findFirst()
+            .map(stmt -> safeStringArgument(sourceId, stmt, "namespace argument"))
+            .map(XMLNamespace::of)
+            .orElseThrow(() -> new IllegalArgumentException("No namespace statement in " + refOf(sourceId, root)));
     }
 
-    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 @NonNull String extractPrefix(final IRStatement root, final SourceIdentifier sourceId) {
+        return root.statements().stream()
+            .filter(stmt -> isBuiltin(stmt, PREFIX))
+            .findFirst()
+            .map(stmt -> safeStringArgument(sourceId, stmt, "prefix argument"))
+            .orElseThrow(() -> new IllegalArgumentException("No prefix statement in " + refOf(sourceId, root)));
     }
 
-    private static ImmutableSet<ModuleImport> parseIncludes(final IRStatement module, final SourceIdentifier source) {
-        final Set<ModuleImport> result = new HashSet<>();
-        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));
-            }
-        }
-        return ImmutableSet.copyOf(result);
+    private static @NonNull Unqualified extractBelongsTo(final IRStatement root, final SourceIdentifier sourceId) {
+        return root.statements().stream()
+            .filter(stmt -> isBuiltin(stmt, BELONGS_TO))
+            .findFirst()
+            .map(stmt -> Unqualified.of(safeStringArgument(sourceId, stmt, "belongs-to module name")))
+            .orElseThrow(() -> new IllegalArgumentException("No belongs-to statement in " + refOf(sourceId, root)));
     }
 
-    private static String getRevisionDateString(final IRStatement importStatement, final SourceIdentifier source) {
-        String revisionDateStr = null;
-        for (final IRStatement substatement : importStatement.statements()) {
-            if (isBuiltin(substatement, REVISION_DATE)) {
-                revisionDateStr = safeStringArgument(source, substatement, "imported module revision-date");
-            }
-        }
-        return revisionDateStr;
+    private static @NonNull ImmutableSet<Revision> extractRevisions(final IRStatement root,
+            final SourceIdentifier sourceId) {
+        return root.statements().stream()
+            .filter(stmt -> isBuiltin(stmt, REVISION))
+            .map(stmt -> Revision.of(safeStringArgument(sourceId, stmt, "revision argument")))
+            .collect(ImmutableSet.toImmutableSet());
+    }
+
+    private static @Nullable Revision extractRevisionDate(final IRStatement root, final SourceIdentifier sourceId) {
+        return root.statements().stream()
+            .filter(stmt -> isBuiltin(stmt, REVISION_DATE))
+            .findFirst()
+            .map(stmt -> Revision.of(safeStringArgument(sourceId, stmt, "revision date argument")))
+            .orElse(null);
+    }
+
+    private static @NonNull ImmutableSet<Import> extractImports(final IRStatement root,
+            final SourceIdentifier sourceId) {
+        return root.statements().stream()
+            .filter(stmt -> isBuiltin(stmt, IMPORT))
+            .map(stmt -> new Import(Unqualified.of(safeStringArgument(sourceId, stmt, "imported module name")),
+                extractPrefix(stmt, sourceId), extractRevisionDate(stmt, sourceId)))
+            .collect(ImmutableSet.toImmutableSet());
+    }
+
+    private static @NonNull ImmutableSet<Include> extractIncludes(final IRStatement root,
+            final SourceIdentifier sourceId) {
+        return root.statements().stream()
+            .filter(stmt -> isBuiltin(stmt, INCLUDE))
+            .map(stmt -> new Include(Unqualified.of(safeStringArgument(sourceId, stmt, "included submodule name")),
+                extractRevisionDate(stmt, sourceId)))
+            .collect(ImmutableSet.toImmutableSet());
+    }
+
+    private static boolean isBuiltin(final IRStatement stmt, final String localName) {
+        return stmt.keyword() instanceof IRKeyword.Unqualified keyword && localName.equals(keyword.identifier());
     }
 
     public static String getLatestRevision(final IRStatement module, final SourceIdentifier source) {
@@ -317,82 +345,20 @@ public abstract class YangModelDependencyInfo {
         return latestRevision;
     }
 
-    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);
-
-        final String latestRevision = getLatestRevision(submodule, source);
-        final ImmutableSet<ModuleImport> imports = parseImports(submodule, source);
-        final ImmutableSet<ModuleImport> includes = parseIncludes(submodule, source);
-
-        return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
-    }
-
-    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");
-            }
+    static @NonNull String safeStringArgument(final SourceIdentifier source, final IRStatement stmt,
+            final String desc) {
+        final var ref = refOf(source, stmt);
+        final var arg = stmt.argument();
+        if (arg == null) {
+            throw new IllegalArgumentException("Missing " + desc + " at " + ref);
         }
-        return null;
-    }
 
-    static String safeStringArgument(final SourceIdentifier source, final IRStatement stmt, final String desc) {
-        final StatementSourceReference ref = getReference(source, stmt);
-        final IRArgument arg = stmt.argument();
-        checkArgument(arg != null, "Missing %s at %s", desc, ref);
         // TODO: we probably need to understand yang version first....
         return ArgumentContextUtils.rfc6020().stringFromStringContext(arg, ref);
     }
 
-    private static StatementSourceReference getReference(final SourceIdentifier source, final IRStatement stmt) {
-        return ExplicitStatement.atPosition(source.getName(), stmt.startLine(), stmt.startColumn() + 1);
-    }
-
-    /**
-     * Dependency information for YANG module.
-     */
-    public static final class ModuleDependencyInfo extends YangModelDependencyInfo {
-        ModuleDependencyInfo(final String name, final String latestRevision, final ImmutableSet<ModuleImport> imports,
-                final ImmutableSet<ModuleImport> includes, final Optional<SemVer> semVer) {
-            super(name, latestRevision, imports, includes, semVer);
-        }
-
-        @Override
-        public String toString() {
-            return "Module [name=" + getName() + ", revision=" + getRevision()
-                + ", semanticVersion=" + getSemanticVersion().orElse(null)
-                + ", dependencies=" + getDependencies()
-                + "]";
-        }
-    }
-
-    /**
-     * Dependency information for submodule, also provides name for parent module.
-     */
-    public static final class SubmoduleDependencyInfo extends YangModelDependencyInfo {
-        private final String belongsTo;
-
-        private SubmoduleDependencyInfo(final String name, final String latestRevision, final String belongsTo,
-                final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
-            super(name, latestRevision, imports, includes);
-            this.belongsTo = belongsTo;
-        }
-
-        /**
-         * Returns name of parent module.
-         */
-        public String getParentModule() {
-            return belongsTo;
-        }
-
-        @Override
-        public String toString() {
-            return "Submodule [name=" + getName() + ", revision="
-                    + getRevision() + ", dependencies=" + getDependencies()
-                    + "]";
-        }
+    private static StatementSourceReference refOf(final SourceIdentifier source, final IRStatement stmt) {
+        return ExplicitStatement.atPosition(source.name().getLocalName(), stmt.startLine(), stmt.startColumn() + 1);
     }
 
     /**
@@ -400,25 +366,22 @@ public abstract class YangModelDependencyInfo {
      */
     // FIXME: this is a rather nasty misuse of APIs :(
     private static final class ModuleImportImpl implements ModuleImport {
-
+        private final @NonNull Unqualified moduleName;
         private final Revision revision;
-        private final SemVer semVer;
-        private final String name;
 
-        ModuleImportImpl(final @NonNull String moduleName, final @Nullable Revision revision) {
-            this(moduleName, revision, null);
+        ModuleImportImpl(final Import importSpec) {
+            moduleName = importSpec.name();
+            revision = importSpec.revision();
         }
 
-        ModuleImportImpl(final @NonNull String moduleName, final @Nullable Revision revision,
-                final @Nullable SemVer semVer) {
-            this.name = requireNonNull(moduleName, "Module name must not be null.");
-            this.revision = revision;
-            this.semVer = semVer;
+        ModuleImportImpl(final Include includeSpec) {
+            moduleName = includeSpec.name();
+            revision = includeSpec.revision();
         }
 
         @Override
-        public String getModuleName() {
-            return name;
+        public Unqualified getModuleName() {
+            return moduleName;
         }
 
         @Override
@@ -426,14 +389,9 @@ public abstract class YangModelDependencyInfo {
             return Optional.ofNullable(revision);
         }
 
-        @Override
-        public Optional<SemVer> getSemanticVersion() {
-            return Optional.ofNullable(semVer);
-        }
-
         @Override
         public String getPrefix() {
-            return null;
+            throw new UnsupportedOperationException();
         }
 
         @Override
@@ -455,29 +413,20 @@ public abstract class YangModelDependencyInfo {
         public int hashCode() {
             final int prime = 31;
             int result = 1;
-            result = prime * result + Objects.hashCode(name);
+            result = prime * result + Objects.hashCode(moduleName);
             result = prime * result + Objects.hashCode(revision);
-            result = prime * result + Objects.hashCode(semVer);
             return result;
         }
 
         @Override
         public boolean equals(final Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (!(obj instanceof ModuleImportImpl)) {
-                return false;
-            }
-            final ModuleImportImpl other = (ModuleImportImpl) obj;
-            return name.equals(other.name) && Objects.equals(revision, other.revision)
-                    && Objects.equals(getSemanticVersion(), other.getSemanticVersion());
+            return this == obj || obj instanceof ModuleImportImpl other
+                && moduleName.equals(other.moduleName) && Objects.equals(revision, other.revision);
         }
 
         @Override
         public String toString() {
-            return "ModuleImportImpl [name=" + name + ", revision="
-                    + QName.formattedRevision(Optional.ofNullable(revision)) + ", semanticVersion=" + semVer + "]";
+            return "ModuleImportImpl [name=" + moduleName + ", revision=" + revision + "]";
         }
     }
 }