Modernize GeneratorReactor
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / GeneratorReactor.java
index db6080c3122468814576fbdcba8d649caf0058a2..fde702375b2184a882cf0084b3a839bc0020a19c 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.mdsal.binding.generator.impl.reactor;
 
-import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Verify.verify;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
@@ -18,9 +17,9 @@ import com.google.common.collect.Maps;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
@@ -33,9 +32,9 @@ import org.opendaylight.yangtools.yang.binding.ChoiceIn;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.PathExpression;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.ri.type.TypeBuilder;
 import org.opendaylight.yangtools.yang.model.spi.ModuleDependencySort;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
@@ -70,6 +69,7 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
     private State state = State.INITIALIZED;
 
     public GeneratorReactor(final EffectiveModelContext context) {
+        super(context);
         inferenceStack = SchemaInferenceStack.of(context);
 
         // Construct modules and their subtrees. Dependency sort is very much needed here, as it establishes order of
@@ -77,10 +77,8 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
         // AugmentGenerators without having forward references.
         // FIXME: migrate to new ModuleDependencySort when it is available, which streamline things here
         children = ModuleDependencySort.sort(context.getModules()).stream()
-            .map(module -> {
-                verify(module instanceof ModuleEffectiveStatement, "Unexpected module %s", module);
-                return new ModuleGenerator((ModuleEffectiveStatement) module);
-            })
+            .map(Module::asEffectiveStatement)
+            .map(ModuleGenerator::new)
             .collect(Collectors.toUnmodifiableList());
         generators = Maps.uniqueIndex(children, gen -> gen.statement().localQNameModule());
     }
@@ -120,25 +118,59 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
         }
 
         // Start measuring time...
-        final Stopwatch sw = Stopwatch.createStarted();
+        final var sw = Stopwatch.createStarted();
 
-        // Step 1a: walk all composite generators and resolve 'uses' statements to the corresponding grouping node,
-        //          establishing implied inheritance ...
+        // Step 1a: Walk all composite generators and resolve 'uses' statements to the corresponding grouping generator,
+        //          establishing implied inheritance. During this walk we maintain 'stack' to aid this process.
+        //          This indirectly triggers resolution of UsesAugmentGenerators' targets by hooking a requirement
+        //          on the resolved grouping's child nodes as needed.
         linkUsesDependencies(children);
 
-        // Step 1b: ... and also link augments and their targets in a separate pass, as we need groupings fully resolved
-        //          before we attempt augmentation lookups ...
-        for (ModuleGenerator module : children) {
-            for (Generator child : module) {
-                if (child instanceof ModuleAugmentGenerator) {
-                    ((ModuleAugmentGenerator) child).linkAugmentationTarget(this);
+        // Step 1b: Walk all module generators and start ModuleAugmentGenerators' target resolution by linking the first
+        //          step of each 'augment' statement to its corresponding instantiated site.
+        //          Then start all UsesAugmentGenerators' target resolution.
+        final var augments = new ArrayList<AugmentRequirement>();
+        for (var module : children) {
+            for (var gen : module) {
+                if (gen instanceof ModuleAugmentGenerator moduleGen) {
+                    augments.add(moduleGen.startLinkage(this));
                 }
             }
         }
+        for (var module : children) {
+            module.startUsesAugmentLinkage(augments);
+        }
+        LOG.trace("Processing linkage of {} augment generators", augments.size());
+
+        // Step 1c: Establish linkage along the reverse uses/augment axis. This is needed to route generated type
+        //          manifestations (isAddedByUses/isAugmenting) to their type generation sites. Since generator tree
+        //          iteration order does not match dependencies, we may need to perform multiple passes.
+        for (var module : children) {
+            verify(module.linkOriginalGenerator(), "Module %s failed to link", module);
+        }
 
-        // Step 1c: ... finally establish linkage along the reverse uses/augment axis. This is needed to route generated
-        //          type manifestations (isAddedByUses/isAugmenting) to their type generation sites.
-        linkOriginalGenerator(children);
+        final var unlinkedModules = new ArrayList<>(children);
+        while (true) {
+            final boolean progress =
+                progressAndClean(unlinkedModules, ModuleGenerator::linkOriginalGeneratorRecursive)
+                // not '||' because we need the side-effects, which would get short-circuited
+                | progressAndClean(augments, AugmentRequirement::resolve);
+
+            if (augments.isEmpty() && unlinkedModules.isEmpty()) {
+                break;
+            }
+
+            if (!progress) {
+                final var ex = new VerifyException("Failed to make progress on linking of original generators");
+                for (var augment : augments) {
+                    ex.addSuppressed(new IllegalStateException(augment + " is incomplete"));
+                }
+                for (var module : unlinkedModules) {
+                    ex.addSuppressed(new IllegalStateException(module + " remains unlinked"));
+                }
+                throw ex;
+            }
+        }
 
         /*
          * Step 2: link typedef statements, so that typedef's 'type' axis is fully established
@@ -178,12 +210,12 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
          * algorithm in CollisionDomain. Note that we may need to walk the domains multiple times, as the process of
          * solving a domain may cause another domain's solution to be invalidated.
          */
-        final List<CollisionDomain> domains = new ArrayList<>();
+        final var domains = new ArrayList<CollisionDomain>();
         collectCollisionDomains(domains, children);
         boolean haveUnresolved;
         do {
             haveUnresolved = false;
-            for (CollisionDomain domain : domains) {
+            for (var domain : domains) {
                 if (domain.findSolution()) {
                     haveUnresolved = true;
                 }
@@ -195,7 +227,7 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
         // We have now properly cross-linked all generators and have assigned their naming roots, so from this point
         // it looks as though we are performing a simple recursive execution. In reality, though, the actual path taken
         // through generators is dictated by us as well as generator linkage.
-        for (ModuleGenerator module : children) {
+        for (var module : children) {
             module.ensureType(builderFactory);
         }
 
@@ -206,29 +238,31 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
 
     private void collectCollisionDomains(final List<CollisionDomain> result,
             final Iterable<? extends Generator> parent) {
-        for (Generator gen : parent) {
+        for (var gen : parent) {
             gen.ensureMember();
             collectCollisionDomains(result, gen);
-            if (gen instanceof AbstractCompositeGenerator) {
-                result.add(((AbstractCompositeGenerator<?>) gen).domain());
+            if (gen instanceof AbstractCompositeGenerator<?, ?> compositeGen) {
+                result.add(compositeGen.domain());
             }
         }
     }
 
     @Override
-    <E extends EffectiveStatement<QName, ?>, G extends AbstractExplicitGenerator<E>> G resolveTreeScoped(
+    <E extends EffectiveStatement<QName, ?>, G extends AbstractExplicitGenerator<E, ?>> G resolveTreeScoped(
             final Class<G> type, final QName argument) {
         LOG.trace("Searching for tree-scoped argument {} at {}", argument, stack);
 
         // Check if the requested QName matches current module, if it does search the stack
-        final Iterable<? extends Generator> last = stack.getLast();
-        verify(last instanceof ModuleGenerator, "Unexpected last stack item %s", last);
+        final var last = stack.getLast();
+        if (!(last instanceof ModuleGenerator lastModule)) {
+            throw new VerifyException("Unexpected last stack item " + last);
+        }
 
-        if (argument.getModule().equals(((ModuleGenerator) last).statement().localQNameModule())) {
-            for (Iterable<? extends Generator> ancestor : stack) {
-                for (Generator child : ancestor) {
+        if (argument.getModule().equals(lastModule.statement().localQNameModule())) {
+            for (var ancestor : stack) {
+                for (var child : ancestor) {
                     if (type.isInstance(child)) {
-                        final G cast = type.cast(child);
+                        final var cast = type.cast(child);
                         if (argument.equals(cast.statement().argument())) {
                             LOG.trace("Found matching {}", child);
                             return cast;
@@ -237,11 +271,11 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
                 }
             }
         } else {
-            final ModuleGenerator module = generators.get(argument.getModule());
+            final var module = generators.get(argument.getModule());
             if (module != null) {
-                for (Generator child : module) {
+                for (var child : module) {
                     if (type.isInstance(child)) {
-                        final G cast = type.cast(child);
+                        final var cast = type.cast(child);
                         if (argument.equals(cast.statement().argument())) {
                             LOG.trace("Found matching {}", child);
                             return cast;
@@ -256,26 +290,31 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
 
     @Override
     ModuleGenerator resolveModule(final QNameModule namespace) {
-        final ModuleGenerator module = generators.get(requireNonNull(namespace));
-        checkState(module != null, "Failed to find module for %s", namespace);
+        final var module = generators.get(requireNonNull(namespace));
+        if (module == null) {
+            throw new IllegalStateException("Failed to find module for " + namespace);
+        }
         return module;
     }
 
     @Override
-    AbstractTypeObjectGenerator<?> resolveLeafref(final PathExpression path) {
+    AbstractTypeObjectGenerator<?, ?> resolveLeafref(final PathExpression path) {
         LOG.trace("Resolving path {}", path);
         verify(inferenceStack.isEmpty(), "Unexpected data tree state %s", inferenceStack);
         try {
             // Populate inferenceStack with a grouping + data tree equivalent of current stack's state.
-            final Iterator<Iterable<? extends Generator>> it = stack.descendingIterator();
+            final var it = stack.descendingIterator();
             // Skip first item, as it points to our children
             verify(it.hasNext(), "Unexpected empty stack");
             it.next();
 
             while (it.hasNext()) {
-                final Iterable<? extends Generator> item = it.next();
-                verify(item instanceof Generator, "Unexpected stack item %s", item);
-                ((Generator) item).pushToInference(inferenceStack);
+                final var item = it.next();
+                if (item instanceof Generator generator) {
+                    generator.pushToInference(inferenceStack);
+                } else {
+                    throw new VerifyException("Unexpected stack item " + item);
+                }
             }
 
             return inferenceStack.inGrouping() ? lenientResolveLeafref(path) : strictResolvePath(path);
@@ -284,7 +323,7 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
         }
     }
 
-    private @NonNull AbstractTypeAwareGenerator<?> strictResolvePath(final @NonNull PathExpression path) {
+    private @NonNull AbstractTypeAwareGenerator<?, ?, ?> strictResolvePath(final @NonNull PathExpression path) {
         try {
             inferenceStack.resolvePathExpression(path);
         } catch (IllegalArgumentException e) {
@@ -293,7 +332,7 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
         return mapToGenerator();
     }
 
-    private @Nullable AbstractTypeAwareGenerator<?> lenientResolveLeafref(final @NonNull PathExpression path) {
+    private @Nullable AbstractTypeAwareGenerator<?, ?, ?> lenientResolveLeafref(final @NonNull PathExpression path) {
         try {
             inferenceStack.resolvePathExpression(path);
         } catch (IllegalArgumentException e) {
@@ -304,27 +343,26 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
     }
 
     // Map a statement to the corresponding generator
-    private @NonNull AbstractTypeAwareGenerator<?> mapToGenerator() {
+    private @NonNull AbstractTypeAwareGenerator<?, ?, ?> mapToGenerator() {
         // Some preliminaries first: we need to be in the correct module to walk the path
-        final ModuleEffectiveStatement module = inferenceStack.currentModule();
-        final ModuleGenerator gen = verifyNotNull(generators.get(module.localQNameModule()),
+        final var module = inferenceStack.currentModule();
+        final var gen = verifyNotNull(generators.get(module.localQNameModule()),
             "Cannot find generator for %s", module);
 
         // Now kick of the search
-        final List<EffectiveStatement<?, ?>> stmtPath = inferenceStack.toInference().statementPath();
-        final AbstractExplicitGenerator<?> found = gen.findGenerator(stmtPath);
-        if (found instanceof AbstractTypeAwareGenerator) {
-            return (AbstractTypeAwareGenerator<?>) found;
+        final var stmtPath = inferenceStack.toInference().statementPath();
+        final var found = gen.findGenerator(stmtPath);
+        if (found instanceof AbstractTypeAwareGenerator<?, ?, ?> typeAware) {
+            return typeAware;
         }
         throw new VerifyException("Statements " + stmtPath + " resulted in unexpected " + found);
     }
 
     // Note: unlike other methods, this method pushes matching child to the stack
     private void linkUsesDependencies(final Iterable<? extends Generator> parent) {
-        for (Generator child : parent) {
-            if (child instanceof AbstractCompositeGenerator) {
-                LOG.trace("Visiting composite {}", child);
-                final AbstractCompositeGenerator<?> composite = (AbstractCompositeGenerator<?>) child;
+        for (var child : parent) {
+            if (child instanceof AbstractCompositeGenerator<?, ?> composite) {
+                LOG.trace("Visiting composite {}", composite);
                 stack.push(composite);
                 composite.linkUsesDependencies(this);
                 linkUsesDependencies(composite);
@@ -333,21 +371,34 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
         }
     }
 
-    private static void linkOriginalGenerator(final Iterable<? extends Generator> parent) {
-        for (Generator child : parent) {
-            if (child instanceof AbstractExplicitGenerator) {
-                ((AbstractExplicitGenerator<?>) child).linkOriginalGenerator();
+    private static <T> boolean progressAndClean(final List<T> items, final Function<T, LinkageProgress> function) {
+        boolean progress = false;
+
+        final var it = items.iterator();
+        while (it.hasNext()) {
+            final var item = it.next();
+            final var tmp = function.apply(item);
+            if (tmp == LinkageProgress.NONE) {
+                LOG.debug("No progress made linking {}", item);
+                continue;
             }
-            if (child instanceof AbstractCompositeGenerator) {
-                linkOriginalGenerator(child);
+
+            progress = true;
+            if (tmp == LinkageProgress.DONE) {
+                LOG.debug("Finished linking {}", item);
+                it.remove();
+            } else {
+                LOG.debug("Progress made linking {}", item);
             }
         }
+
+        return progress;
     }
 
     private void linkDependencies(final Iterable<? extends Generator> parent) {
-        for (Generator child : parent) {
-            if (child instanceof AbstractDependentGenerator) {
-                ((AbstractDependentGenerator<?>) child).linkDependencies(this);
+        for (var child : parent) {
+            if (child instanceof AbstractDependentGenerator<?, ?> dependent) {
+                dependent.linkDependencies(this);
             } else if (child instanceof AbstractCompositeGenerator) {
                 stack.push(child);
                 linkDependencies(child);
@@ -357,10 +408,10 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
     }
 
     private void bindTypeDefinition(final Iterable<? extends Generator> parent) {
-        for (Generator child : parent) {
+        for (var child : parent) {
             stack.push(child);
-            if (child instanceof AbstractTypeObjectGenerator) {
-                ((AbstractTypeObjectGenerator<?>) child).bindTypeDefinition(this);
+            if (child instanceof AbstractTypeObjectGenerator<?, ?> typeObject) {
+                typeObject.bindTypeDefinition(this);
             } else if (child instanceof AbstractCompositeGenerator) {
                 bindTypeDefinition(child);
             }