X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-generator%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fgenerator%2Fimpl%2Freactor%2FGeneratorReactor.java;h=fde702375b2184a882cf0084b3a839bc0020a19c;hb=01dd79f2bff48d983d808060b2328c146f97ca70;hp=db6080c3122468814576fbdcba8d649caf0058a2;hpb=2eff05d583386defce027c7ad60995a233c9ab9e;p=mdsal.git diff --git a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/GeneratorReactor.java b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/GeneratorReactor.java index db6080c312..fde702375b 100644 --- a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/GeneratorReactor.java +++ b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/GeneratorReactor.java @@ -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(); + 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 domains = new ArrayList<>(); + final var domains = new ArrayList(); 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 result, final Iterable 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 - , G extends AbstractExplicitGenerator> G resolveTreeScoped( + , G extends AbstractExplicitGenerator> G resolveTreeScoped( final Class 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 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 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> 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 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> 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 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 parent) { - for (Generator child : parent) { - if (child instanceof AbstractExplicitGenerator) { - ((AbstractExplicitGenerator) child).linkOriginalGenerator(); + private static boolean progressAndClean(final List items, final Function 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 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 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); }