Eliminate YangModelDependencyInfo
[yangtools.git] / model / yang-model-spi / src / main / java / org / opendaylight / yangtools / yang / model / spi / source / SourceInfo.java
index e159819457f5d4c406ed1cf4255fdc79dc0b4064..6e830465176ae6496b8c86a5cc0e692cb82ae20d 100644 (file)
@@ -10,20 +10,33 @@ package org.opendaylight.yangtools.yang.model.spi.source;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableSet;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.ArrayList;
+import java.util.Comparator;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 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.model.api.source.SourceDependency.BelongsTo;
+import org.opendaylight.yangtools.yang.model.api.source.SourceDependency.Import;
+import org.opendaylight.yangtools.yang.model.api.source.SourceDependency.Include;
+import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RootDeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionStatement;
 
 /**
  * Linkage information about a particular {@link SourceRepresentation}. It has two specializations
  * <ol>
- *   <li>{@link ModuleSourceInfo} pertaining to {@link SourceRepresentation} which have {@code module} as its root
- *       statement</li>
- *   <li>{@link SubmoduleSourceInfo} pertaining to {@link SourceRepresentation} which have {@code submodule} as its root
+ *   <li>{@link SourceInfo.Module} pertaining to {@link SourceRepresentation} which have {@code module} as its root
  *       statement</li>
+ *   <li>{@link SourceInfo.Submodule} pertaining to {@link SourceRepresentation} which have {@code submodule} as its
+ *       root statement</li>
  * </ol>
  *
  * <p>
@@ -36,38 +49,199 @@ import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
  * </ul>
  */
 @NonNullByDefault
-public sealed interface SourceInfo permits ModuleSourceInfo, SubmoduleSourceInfo {
-    record Import(Unqualified name, String prefix, @Nullable Revision revision) {
-        public Import {
-            requireNonNull(name);
-            requireNonNull(prefix);
-        }
-    }
-
-    record Include(Unqualified name, @Nullable Revision revision) {
-        public Include {
-            requireNonNull(name);
-        }
-    }
-
+public sealed interface SourceInfo permits SourceInfo.Module, SourceInfo.Submodule {
     /**
-     * The name of this source, as expressed by the argument of {@code module} or {@code submodule} statement.
+     * Return the {@link SourceIdentifier} of this source, as expressed by {@link RootDeclaredStatement#argument()}
+     * contract coupled with the first entry in {@link #revisions()}.
      *
      * @return name of this source.
      */
-    Unqualified name();
+    SourceIdentifier sourceId();
 
     /**
-     * {@link YangVersion} of the source. If no {@code yang-version} is present, this method will return
+     * Return {@link YangVersion} of the source. If no {@link YangVersionStatement} is present, this method will return
      * {@link YangVersion#VERSION_1}.
      *
      * @return {@link YangVersion} of the source
      */
     YangVersion yangVersion();
 
+    /**
+     * The set of all {@link RevisionDateStatement} mentioned in {@link RevisionStatement}s. The returned set is ordered
+     * in reverse order, i.e. newest revision is encountered first.
+     *
+     * @return all revisions known by this source
+     */
     ImmutableSet<Revision> revisions();
 
+    /**
+     * Return all {@link Import} dependencies.
+     *
+     * @return all import dependencies
+     */
     ImmutableSet<Import> imports();
 
+    /**
+     * Return all {@link Include} dependencies.
+     *
+     * @return all include dependencies
+     */
     ImmutableSet<Include> includes();
+
+    /**
+     * A {@link SourceInfo} about a {@link ModuleStatement}-backed source.
+     */
+    record Module(
+            SourceIdentifier sourceId,
+            YangVersion yangVersion,
+            XMLNamespace namespace,
+            Unqualified prefix,
+            ImmutableSet<Revision> revisions,
+            ImmutableSet<Import> imports,
+            ImmutableSet<Include> includes) implements SourceInfo {
+        public Module {
+            requireNonNull(sourceId);
+            requireNonNull(yangVersion);
+            requireNonNull(namespace);
+            requireNonNull(prefix);
+            requireNonNull(revisions);
+            requireNonNull(imports);
+            requireNonNull(includes);
+        }
+
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        public static final class Builder extends SourceInfo.Builder<Builder, Module> {
+            @SuppressFBWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
+                justification = "https://github.com/spotbugs/spotbugs/issues/743")
+            private @Nullable XMLNamespace namespace;
+            @SuppressFBWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
+                justification = "https://github.com/spotbugs/spotbugs/issues/743")
+            private @Nullable Unqualified prefix;
+
+            Builder() {
+                // Hidden on purpose
+            }
+
+            public Builder setNamespace(final XMLNamespace namespace) {
+                this.namespace = requireNonNull(namespace);
+                return this;
+            }
+
+            public Builder setPrefix(final Unqualified prefix) {
+                this.prefix = requireNonNull(prefix);
+                return this;
+            }
+
+            @Override
+            Module buildInstance(final SourceIdentifier sourceId, final YangVersion yangVersion,
+                    final ImmutableSet<Revision> revisions, final ImmutableSet<Import> imports,
+                    final ImmutableSet<Include> includes) {
+                return new Module(sourceId, yangVersion, requireNonNull(namespace), requireNonNull(prefix), revisions,
+                    imports, includes);
+            }
+        }
+    }
+
+    /**
+     * A {@link SourceInfo} about a {@code submodule}.
+     */
+    record Submodule(
+            SourceIdentifier sourceId,
+            YangVersion yangVersion,
+            BelongsTo belongsTo,
+            ImmutableSet<Revision> revisions,
+            ImmutableSet<Import> imports,
+            ImmutableSet<Include> includes) implements SourceInfo {
+        public Submodule {
+            requireNonNull(sourceId);
+            requireNonNull(yangVersion);
+            requireNonNull(belongsTo);
+            requireNonNull(revisions);
+            requireNonNull(imports);
+            requireNonNull(imports);
+            requireNonNull(includes);
+        }
+
+        public static Builder builder() {
+            return new Builder();
+        }
+
+        public static final class Builder extends SourceInfo.Builder<Builder, Submodule> {
+            @SuppressFBWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
+                justification = "https://github.com/spotbugs/spotbugs/issues/743")
+            private @Nullable BelongsTo belongsTo;
+
+            Builder() {
+                // Hidden on purpose
+            }
+
+            public Builder setBelongsTo(final BelongsTo belongsTo) {
+                this.belongsTo = requireNonNull(belongsTo);
+                return this;
+            }
+
+            @Override
+            Submodule buildInstance(final SourceIdentifier sourceId, final YangVersion yangVersion,
+                    final ImmutableSet<Revision> revisions, final ImmutableSet<Import> imports,
+                    final ImmutableSet<Include> includes) {
+                return new Submodule(sourceId, yangVersion, requireNonNull(belongsTo), revisions, imports, includes);
+            }
+        }
+    }
+
+    abstract sealed class Builder<B extends Builder<B, I>, I extends SourceInfo> {
+        private final ImmutableSet.Builder<Import> imports = ImmutableSet.builder();
+        private final ImmutableSet.Builder<Include> includes = ImmutableSet.builder();
+        private final ArrayList<Revision> revisions = new ArrayList<>();
+        private YangVersion yangVersion = YangVersion.VERSION_1;
+        @SuppressFBWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
+            justification = "https://github.com/spotbugs/spotbugs/issues/743")
+        private @Nullable Unqualified name;
+
+        public final B setName(final Unqualified newName) {
+            name = requireNonNull(newName);
+            return thisInstance();
+        }
+
+        public final B setYangVersion(final YangVersion newYangVersion) {
+            yangVersion = requireNonNull(newYangVersion);
+            return thisInstance();
+        }
+
+        public final B addImport(final Import importDep) {
+            imports.add(importDep);
+            return thisInstance();
+        }
+
+        public final B addInclude(final Include includeDep) {
+            includes.add(includeDep);
+            return thisInstance();
+        }
+
+        public final B addRevision(final Revision revision) {
+            revisions.add(revision);
+            return thisInstance();
+        }
+
+        public final I build() {
+            final var sorted = revisions.stream()
+                .sorted(Comparator.reverseOrder())
+                .collect(ImmutableSet.toImmutableSet());
+
+            return buildInstance(
+                new SourceIdentifier(requireNonNull(name), sorted.isEmpty() ? null : sorted.iterator().next()),
+                yangVersion, sorted, imports.build(), includes.build());
+        }
+
+        abstract I buildInstance(SourceIdentifier sourceId, YangVersion yangVersion, ImmutableSet<Revision> revisions,
+            ImmutableSet<Import> imports, ImmutableSet<Include> includes);
+
+        @SuppressWarnings("unchecked")
+        private B thisInstance() {
+            return (B) this;
+        }
+    }
 }