BUG-4688: switch revisions from Date to Revision
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / repo / DependencyResolver.java
index 45ad366285af4c7f5244ebed4ff22c6b3429e336..f1edb2d920cfa92cd2b753d5393f5efe56597422 100644 (file)
@@ -7,18 +7,21 @@
  */
 package org.opendaylight.yangtools.yang.parser.repo;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Multimap;
-
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
-
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
@@ -30,50 +33,20 @@ import org.slf4j.LoggerFactory;
  * corresponding dependency information, the {@link #create(Map)} method creates a
  * a view of how consistent the dependencies are. In particular, this detects whether
  * any imports are unsatisfied.
- *
- * FIXME: improve this class to track and expose how wildcard imports were resolved.
- *        That information will allow us to track "damage" to dependency resolution
- *        as new models are added to a schema context.
  */
-final class DependencyResolver {
+// FIXME: improve this class to track and expose how wildcard imports were resolved.
+//        That information will allow us to track "damage" to dependency resolution
+//        as new models are added to a schema context.
+abstract class DependencyResolver {
     private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class);
     private final Collection<SourceIdentifier> resolvedSources;
     private final Collection<SourceIdentifier> unresolvedSources;
     private final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
 
-    public DependencyResolver(final Collection<SourceIdentifier> resolvedSources,
-            final Collection<SourceIdentifier> unresolvedSources, final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
-        this.resolvedSources = Preconditions.checkNotNull(resolvedSources);
-        this.unresolvedSources = Preconditions.checkNotNull(unresolvedSources);
-        this.unsatisfiedImports = Preconditions.checkNotNull(unsatisfiedImports);
-    }
-
-    private static SourceIdentifier findWildcard(final Iterable<SourceIdentifier> haystack, final String needle) {
-        for (SourceIdentifier r : haystack) {
-            if (r.getName().equals(needle)) {
-                return r;
-            }
-        }
-
-        return null;
-    }
-
-    private static boolean isKnown(final Collection<SourceIdentifier> haystack, final ModuleImport mi) {
-        final String rev = mi.getRevision() != null ? mi.getRevision().toString() : null;
-        final SourceIdentifier msi = SourceIdentifier.create(mi.getModuleName(), Optional.fromNullable(rev));
-
-        // Quick lookup
-        if (haystack.contains(msi)) {
-            return true;
-        }
-
-        // Slow revision-less walk
-        return rev == null && findWildcard(haystack, mi.getModuleName()) != null;
-    }
-
-    public static final DependencyResolver create(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+    protected DependencyResolver(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
         final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
         final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
+        final Map<SourceIdentifier, BelongsToDependency> submodules = new HashMap<>();
 
         boolean progress;
         do {
@@ -85,7 +58,16 @@ final class DependencyResolver {
                 final YangModelDependencyInfo dep = depInfo.get(id);
 
                 boolean okay = true;
-                for (ModuleImport mi : dep.getDependencies()) {
+
+                final Set<ModuleImport> dependencies = dep.getDependencies();
+
+                // in case of submodule, remember belongs to
+                if (dep instanceof YangModelDependencyInfo.SubmoduleDependencyInfo) {
+                    final String parent = ((YangModelDependencyInfo.SubmoduleDependencyInfo) dep).getParentModule();
+                    submodules.put(id, new BelongsToDependency(parent));
+                }
+
+                for (final ModuleImport mi : dependencies) {
                     if (!isKnown(resolved, mi)) {
                         LOG.debug("Source {} is missing import {}", id, mi);
                         okay = false;
@@ -102,27 +84,36 @@ final class DependencyResolver {
             }
         } while (progress);
 
-        if (!pending.isEmpty()) {
-            final Multimap<SourceIdentifier, ModuleImport> imports = ArrayListMultimap.create();
-            for (SourceIdentifier id : pending) {
-                final YangModelDependencyInfo dep = depInfo.get(id);
-                for (ModuleImport mi : dep.getDependencies()) {
-                    if (!isKnown(pending, mi) && !isKnown(resolved, mi)) {
-                        imports.put(id, mi);
-                    }
-                }
+        /// Additional check only for belongs-to statement
+        for (final Entry<SourceIdentifier, BelongsToDependency> submodule : submodules.entrySet()) {
+            final BelongsToDependency belongs = submodule.getValue();
+            final SourceIdentifier sourceIdentifier = submodule.getKey();
+            if (!isKnown(resolved, belongs)) {
+                LOG.debug("Source {} is missing parent {}", sourceIdentifier, belongs);
+                pending.add(sourceIdentifier);
+                resolved.remove(sourceIdentifier);
             }
+        }
 
-            return new DependencyResolver(resolved, pending, imports);
-        } else {
-            return new DependencyResolver(resolved, Collections.<SourceIdentifier>emptyList(), ImmutableMultimap.<SourceIdentifier, ModuleImport>of());
+        final Multimap<SourceIdentifier, ModuleImport> imports = ArrayListMultimap.create();
+        for (final SourceIdentifier id : pending) {
+            final YangModelDependencyInfo dep = depInfo.get(id);
+            for (final ModuleImport mi : dep.getDependencies()) {
+                if (!isKnown(pending, mi) && !isKnown(resolved, mi)) {
+                    imports.put(id, mi);
+                }
+            }
         }
+
+        this.resolvedSources = ImmutableList.copyOf(resolved);
+        this.unresolvedSources = ImmutableList.copyOf(pending);
+        this.unsatisfiedImports = ImmutableMultimap.copyOf(imports);
     }
 
+    protected abstract boolean isKnown(Collection<SourceIdentifier> haystack, ModuleImport mi);
+
     /**
      * Collection of sources which have been resolved.
-     *
-     * @return
      */
     Collection<SourceIdentifier> getResolvedSources() {
         return resolvedSources;
@@ -130,8 +121,6 @@ final class DependencyResolver {
 
     /**
      * Collection of sources which have not been resolved due to missing dependencies.
-     *
-     * @return
      */
     Collection<SourceIdentifier> getUnresolvedSources() {
         return unresolvedSources;
@@ -142,20 +131,54 @@ final class DependencyResolver {
      * is the source identifier of module which was issuing an import, the values
      * are imports which were unsatisfied.
      *
+     * <p>
      * Note that this map contains only imports which are missing from the reactor,
      * not transitive failures.
      *
+     * <p>
      * Examples:
-     *
+     * <ul><li>
      * If A imports B, B imports C, and both A and B are in the reactor, only B->C
      * will be reported.
-     *
+     * </li><li>
      * If A imports B and C, B imports C, and both A and B are in the reactor,
      * A->C and B->C will be reported.
-     *
-     * @return
+     * </li></ul>
      */
     Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
         return unsatisfiedImports;
     }
+
+    private static class BelongsToDependency implements ModuleImport {
+        private final String parent;
+
+        BelongsToDependency(final String parent) {
+            this.parent = parent;
+        }
+
+        @Override
+        public String getModuleName() {
+            return parent;
+        }
+
+        @Override
+        public Optional<Revision> getRevision() {
+            return Optional.empty();
+        }
+
+        @Override
+        public String getPrefix() {
+            return null;
+        }
+
+        @Override
+        public Optional<SemVer> getSemanticVersion() {
+            return Optional.empty();
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("parent", parent).toString();
+        }
+    }
 }