*/
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;
import com.google.common.base.Stopwatch;
import com.google.common.base.VerifyException;
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;
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.api.stmt.SchemaNodeIdentifier;
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;
private final @NonNull List<ModuleGenerator> children;
private final @NonNull SchemaInferenceStack inferenceStack;
- private Map<?, AbstractTypeAwareGenerator<?>> leafGenerators;
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
throw new IllegalStateException("Unhandled state" + state);
}
- // Step 1a: walk all composite generators and resolve 'uses' statements to the corresponding grouping node,
- // establishing implied inheritance ...
+ // Start measuring time...
+ final Stopwatch sw = Stopwatch.createStarted();
+
+ // 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 ...
+ // 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 (ModuleGenerator module : children) {
- for (Generator child : module) {
- if (child instanceof ModuleAugmentGenerator) {
- ((ModuleAugmentGenerator) child).linkAugmentationTarget(this);
+ for (Generator gen : module) {
+ if (gen instanceof ModuleAugmentGenerator) {
+ augments.add(((ModuleAugmentGenerator) gen).startLinkage(this));
}
}
}
+ for (ModuleGenerator module : children) {
+ module.startUsesAugmentLinkage(augments);
+ }
+ LOG.trace("Processing linkage of {} augment generators", augments.size());
- // 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);
+ // 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 (ModuleGenerator module : children) {
+ verify(module.linkOriginalGenerator(), "Module %s failed to link", module);
+ }
+
+ 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
* Since our implementation class hierarchy captures all four statements involved in a common superclass, we can
* perform this in a single pass.
*/
- final Stopwatch sw = Stopwatch.createStarted();
linkDependencies(children);
// Step five: resolve all 'type leafref' and 'type identityref' statements, so they point to their
gen.ensureMember();
collectCollisionDomains(result, gen);
if (gen instanceof AbstractCompositeGenerator) {
- result.add(((AbstractCompositeGenerator<?>) gen).domain());
+ result.add(((AbstractCompositeGenerator<?, ?>) gen).domain());
}
}
}
@Override
- AbstractExplicitGenerator<?> resolveSchemaNode(final SchemaNodeIdentifier path) {
- verify(path instanceof SchemaNodeIdentifier.Absolute, "Unexpected path %s", path);
- return verifyNotNull(generators.get(path.firstNodeIdentifier().getModule()), "Cannot find module for %s", path)
- .resolveSchemaNode(path, null);
- }
-
- @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);
}
@Override
- IdentityGenerator resolveIdentity(final QName name) {
- final ModuleGenerator module = generators.get(name.getModule());
- if (module != null) {
- for (Generator gen : module) {
- if (gen instanceof IdentityGenerator) {
- final IdentityGenerator idgen = (IdentityGenerator) gen;
- if (name.equals(idgen.statement().argument())) {
- return idgen;
- }
- }
- }
- }
- throw new IllegalStateException("Failed to find identity " + name);
+ ModuleGenerator resolveModule(final QNameModule namespace) {
+ final ModuleGenerator module = generators.get(requireNonNull(namespace));
+ checkState(module != null, "Failed to find module for %s", 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 {
}
}
- private @NonNull AbstractTypeAwareGenerator<?> strictResolvePath(final @NonNull PathExpression path) {
+ private @NonNull AbstractTypeAwareGenerator<?, ?, ?> strictResolvePath(final @NonNull PathExpression path) {
try {
inferenceStack.resolvePathExpression(path);
} catch (IllegalArgumentException e) {
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) {
}
// 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()),
// Now kick of the search
final List<EffectiveStatement<?, ?>> stmtPath = inferenceStack.toInference().statementPath();
- final AbstractExplicitGenerator<?> found = gen.findGenerator(stmtPath);
+ final AbstractExplicitGenerator<?, ?> found = gen.findGenerator(stmtPath);
if (found instanceof AbstractTypeAwareGenerator) {
- return (AbstractTypeAwareGenerator<?>) found;
+ return (AbstractTypeAwareGenerator<?, ?, ?>) found;
}
throw new VerifyException("Statements " + stmtPath + " resulted in unexpected " + found);
}
for (Generator child : parent) {
if (child instanceof AbstractCompositeGenerator) {
LOG.trace("Visiting composite {}", child);
- final AbstractCompositeGenerator<?> composite = (AbstractCompositeGenerator<?>) child;
+ final var composite = (AbstractCompositeGenerator<?, ?>) child;
stack.push(composite);
composite.linkUsesDependencies(this);
linkUsesDependencies(composite);
}
}
- private void linkDependencies(final Iterable<? extends Generator> parent) {
- for (Generator child : parent) {
- if (child instanceof AbstractDependentGenerator) {
- ((AbstractDependentGenerator<?>) child).linkDependencies(this);
- } else if (child instanceof AbstractCompositeGenerator) {
- stack.push(child);
- linkDependencies(child);
- stack.pop();
+ 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;
+ }
+
+ progress = true;
+ if (tmp == LinkageProgress.DONE) {
+ LOG.debug("Finished linking {}", item);
+ it.remove();
+ } else {
+ LOG.debug("Progress made linking {}", item);
}
}
+
+ return progress;
}
- private void linkOriginalGenerator(final Iterable<? extends Generator> parent) {
+ private void linkDependencies(final Iterable<? extends Generator> parent) {
for (Generator child : parent) {
- if (child instanceof AbstractExplicitGenerator) {
- ((AbstractExplicitGenerator<?>) child).linkOriginalGenerator(this);
- }
- if (child instanceof AbstractCompositeGenerator) {
+ if (child instanceof AbstractDependentGenerator) {
+ ((AbstractDependentGenerator<?, ?>) child).linkDependencies(this);
+ } else if (child instanceof AbstractCompositeGenerator) {
stack.push(child);
- linkOriginalGenerator(child);
+ linkDependencies(child);
stack.pop();
}
}
for (Generator child : parent) {
stack.push(child);
if (child instanceof AbstractTypeObjectGenerator) {
- ((AbstractTypeObjectGenerator<?>) child).bindTypeDefinition(this);
+ ((AbstractTypeObjectGenerator<?, ?>) child).bindTypeDefinition(this);
} else if (child instanceof AbstractCompositeGenerator) {
bindTypeDefinition(child);
}