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%2FAbstractCompositeGenerator.java;h=d4ed8314b06f2144f7ee8fd33256256b6c0bcec0;hb=feaecf128a14832eec9661057af4de6b3704a07f;hp=14889c53dd0642a6a34f7988564375a506ce72a6;hpb=73f9223a21ad3c7f42290af10f793723f5d10784;p=mdsal.git diff --git a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractCompositeGenerator.java b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractCompositeGenerator.java index 14889c53dd..d4ed8314b0 100644 --- a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractCompositeGenerator.java +++ b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractCompositeGenerator.java @@ -8,15 +8,18 @@ package org.opendaylight.mdsal.binding.generator.impl.reactor; import static com.google.common.base.Verify.verify; -import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.mdsal.binding.generator.impl.tree.SchemaTreeChild; +import org.opendaylight.mdsal.binding.generator.impl.tree.SchemaTreeParent; import org.opendaylight.mdsal.binding.model.api.Enumeration; import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject; import org.opendaylight.mdsal.binding.model.api.GeneratedType; @@ -53,34 +56,133 @@ import org.slf4j.LoggerFactory; * A composite generator. Composite generators may contain additional children, which end up being mapped into * the naming hierarchy 'under' the composite generator. To support this use case, each composite has a Java package * name assigned. + * + *

+ * State tracking for resolution of children to their original declaration, i.e. back along the 'uses' and 'augment' + * axis. This is quite convoluted because we are traversing the generator tree recursively in the iteration order of + * children, but actual dependencies may require resolution in a different order, for example in the case of: + *

+ *   container foo {
+ *     uses bar {             // A
+ *       augment bar {        // B
+ *         container xyzzy;   // C
+ *       }
+ *     }
+ *
+ *     grouping bar {
+ *       container bar {      // D
+ *         uses baz;          // E
+ *       }
+ *     }
+ *
+ *     grouping baz {
+ *       leaf baz {           // F
+ *         type string;
+ *       }
+ *     }
+ *   }
+ *
+ *   augment /foo/bar/xyzzy { // G
+ *     leaf xyzzy {           // H
+ *       type string;
+ *     }
+ *   }
+ * 
+ * + *

+ * In this case we have three manifestations of 'leaf baz' -- marked A, E and F in the child iteration order. In order + * to perform a resolution, we first have to determine that F is the original definition, then establish that E is using + * the definition made by F and finally establish that A is using the definition made by F. + * + *

+ * Dealing with augmentations is harder still, because we need to attach them to the original definition, hence for the + * /foo/bar container at A, we need to understand that its original definition is at D and we need to attach the augment + * at B to D. Futhermore we also need to establish that the augmentation at G attaches to container defined in C, so + * that the 'leaf xyzzy' existing as /foo/bar/xyzzy/xyzzy under C has its original definition at H. + * + *

+ * Finally realize that the augment at G can actually exist in a different module and is shown in this example only + * the simplified form. That also means we could encounter G well before 'container foo' as well as we can have multiple + * such augments sprinkled across multiple modules having the same dependency rules as between C and G -- but they still + * have to form a directed acyclic graph and we partially deal with those complexities by having modules sorted by their + * dependencies. + * + *

+ * For further details see {@link #linkOriginalGenerator()} and {@link #linkOriginalGeneratorRecursive()}, which deal + * with linking original instances in the tree iteration order. The part dealing with augment attachment lives mostly + * in {@link AugmentRequirement}. */ -abstract class AbstractCompositeGenerator> extends AbstractExplicitGenerator { +public abstract class AbstractCompositeGenerator> + extends AbstractExplicitGenerator implements SchemaTreeParent { private static final Logger LOG = LoggerFactory.getLogger(AbstractCompositeGenerator.class); + // FIXME: we want to allocate this lazily to lower memory footprint private final @NonNull CollisionDomain domain = new CollisionDomain(this); - private final List children; + private final @NonNull List childGenerators; + /** + * {@link SchemaTreeChild} children of this generator. Generator linkage is ensured on first access. + */ + private final @NonNull List> schemaTreeChildren; + /** + * List of {@code augment} statements targeting this generator. This list is maintained only for the primary + * incarnation. This list is an evolving entity until after we have finished linkage of original statements. It is + * expected to be stable at the start of {@code step 2} in {@link GeneratorReactor#execute(TypeBuilderFactory)}. + */ private List augments = List.of(); + + /** + * List of {@code grouping} statements this statement references. This field is set once by + * {@link #linkUsesDependencies(GeneratorContext)}. + */ private List groupings; + /** + * List of composite children which have not been recursively processed. This may become a mutable list when we + * have some children which have not completed linking. Once we have completed linking of all children, including + * {@link #unlinkedChildren}, this will be set to {@code null}. + */ + private List> unlinkedComposites = List.of(); + /** + * List of children which have not had their original linked. This list starts of as null. When we first attempt + * linkage, it becomes non-null. + */ + private List unlinkedChildren; + AbstractCompositeGenerator(final T statement) { super(statement); - children = createChildren(statement); + + final var children = createChildren(statement); + childGenerators = children.getKey(); + schemaTreeChildren = children.getValue(); } AbstractCompositeGenerator(final T statement, final AbstractCompositeGenerator parent) { super(statement, parent); - children = createChildren(statement); + + final var children = createChildren(statement); + childGenerators = children.getKey(); + schemaTreeChildren = children.getValue(); } @Override public final Iterator iterator() { - return children.iterator(); + return childGenerators.iterator(); + } + + @Override + public List> schemaTreeChildren() { + for (var child : schemaTreeChildren) { + if (child instanceof SchemaTreePlaceholder) { + ((SchemaTreePlaceholder) child).setGenerator(this); + } + } + return schemaTreeChildren; } @Override final boolean isEmpty() { - return children.isEmpty(); + return childGenerators.isEmpty(); } final @Nullable AbstractExplicitGenerator findGenerator(final List> stmtPath) { @@ -93,7 +195,7 @@ abstract class AbstractCompositeGenerator> ex final EffectiveStatement stmt = stmtPath.get(offset); // Try direct children first, which is simple - AbstractExplicitGenerator ret = childStrategy.findGenerator(stmt, children); + AbstractExplicitGenerator ret = childStrategy.findGenerator(stmt, childGenerators); if (ret != null) { final int next = offset + 1; if (stmtPath.size() == next) { @@ -142,7 +244,9 @@ abstract class AbstractCompositeGenerator> ex final void linkUsesDependencies(final GeneratorContext context) { // We are establishing two linkages here: // - we are resolving 'uses' statements to their corresponding 'grouping' definitions - // - we propagate those groupings as anchors to any augment statements + // - we propagate those groupings as anchors to any augment statements, which takes out some amount of guesswork + // from augment+uses resolution case, as groupings know about their immediate augments as soon as uses linkage + // is resolved final List tmp = new ArrayList<>(); for (EffectiveStatement stmt : statement().effectiveSubstatements()) { if (stmt instanceof UsesEffectiveStatement) { @@ -150,9 +254,11 @@ abstract class AbstractCompositeGenerator> ex final GroupingGenerator grouping = context.resolveTreeScoped(GroupingGenerator.class, uses.argument()); tmp.add(grouping); + // Trigger resolution of uses/augment statements. This looks like guesswork, but there may be multiple + // 'augment' statements in a 'uses' statement and keeping a ListMultimap here seems wasteful. for (Generator gen : this) { if (gen instanceof UsesAugmentGenerator) { - ((UsesAugmentGenerator) gen).linkGroupingDependency(uses, grouping); + ((UsesAugmentGenerator) gen).resolveGrouping(uses, grouping); } } } @@ -160,6 +266,17 @@ abstract class AbstractCompositeGenerator> ex groupings = List.copyOf(tmp); } + final void startUsesAugmentLinkage(final List requirements) { + for (Generator child : childGenerators) { + if (child instanceof UsesAugmentGenerator) { + requirements.add(((UsesAugmentGenerator) child).startLinkage()); + } + if (child instanceof AbstractCompositeGenerator) { + ((AbstractCompositeGenerator) child).startUsesAugmentLinkage(requirements); + } + } + } + final void addAugment(final AbstractAugmentGenerator augment) { if (augments.isEmpty()) { augments = new ArrayList<>(2); @@ -167,45 +284,142 @@ abstract class AbstractCompositeGenerator> ex augments.add(requireNonNull(augment)); } + /** + * Attempt to link the generator corresponding to the original definition for this generator's statements as well as + * to all child generators. + * + * @return Progress indication + */ + final @NonNull LinkageProgress linkOriginalGeneratorRecursive() { + if (unlinkedComposites == null) { + // We have unset this list (see below), and there is nothing left to do + return LinkageProgress.DONE; + } + + if (unlinkedChildren == null) { + unlinkedChildren = childGenerators.stream() + .filter(AbstractExplicitGenerator.class::isInstance) + .map(child -> (AbstractExplicitGenerator) child) + .collect(Collectors.toList()); + } + + var progress = LinkageProgress.NONE; + if (!unlinkedChildren.isEmpty()) { + // Attempt to make progress on child linkage + final var it = unlinkedChildren.iterator(); + while (it.hasNext()) { + final var child = it.next(); + if (child instanceof AbstractExplicitGenerator) { + if (((AbstractExplicitGenerator) child).linkOriginalGenerator()) { + progress = LinkageProgress.SOME; + it.remove(); + + // If this is a composite generator we need to process is further + if (child instanceof AbstractCompositeGenerator) { + if (unlinkedComposites.isEmpty()) { + unlinkedComposites = new ArrayList<>(); + } + unlinkedComposites.add((AbstractCompositeGenerator) child); + } + } + } + } + + if (unlinkedChildren.isEmpty()) { + // Nothing left to do, make sure any previously-allocated list can be scavenged + unlinkedChildren = List.of(); + } + } + + // Process children of any composite children we have. + final var it = unlinkedComposites.iterator(); + while (it.hasNext()) { + final var tmp = it.next().linkOriginalGeneratorRecursive(); + if (tmp != LinkageProgress.NONE) { + progress = LinkageProgress.SOME; + } + if (tmp == LinkageProgress.DONE) { + it.remove(); + } + } + + if (unlinkedChildren.isEmpty() && unlinkedComposites.isEmpty()) { + // All done, set the list to null to indicate there is nothing left to do in this generator or any of our + // children. + unlinkedComposites = null; + return LinkageProgress.DONE; + } + + return progress; + } + @Override - final AbstractCompositeGenerator getOriginal() { - return (AbstractCompositeGenerator) super.getOriginal(); + final AbstractCompositeGenerator getOriginal() { + return (AbstractCompositeGenerator) super.getOriginal(); } - final @NonNull AbstractExplicitGenerator getOriginalChild(final QName childQName) { + @Override + final AbstractCompositeGenerator tryOriginal() { + return (AbstractCompositeGenerator) super.tryOriginal(); + } + + final > @Nullable OriginalLink originalChild(final QName childQName) { // First try groupings/augments ... - final AbstractExplicitGenerator found = findInferredGenerator(childQName); + var found = findInferredGenerator(childQName); if (found != null) { - return found; + return (OriginalLink) OriginalLink.partial(found); } // ... no luck, we really need to start looking at our origin - final AbstractExplicitGenerator prev = verifyNotNull(previous(), - "Failed to find %s in scope of %s", childQName, this); + final var prev = previous(); + if (prev != null) { + final QName prevQName = childQName.bindTo(prev.getQName().getModule()); + found = prev.findSchemaTreeGenerator(prevQName); + if (found != null) { + return (OriginalLink) found.originalLink(); + } + } - final QName prevQName = childQName.bindTo(prev.getQName().getModule()); - return verifyNotNull(prev.findSchemaTreeGenerator(prevQName), - "Failed to find child %s (proxy for %s) in %s", prevQName, childQName, prev).getOriginal(); + return null; } @Override - final @Nullable AbstractExplicitGenerator findSchemaTreeGenerator(final QName qname) { + final AbstractExplicitGenerator findSchemaTreeGenerator(final QName qname) { final AbstractExplicitGenerator found = super.findSchemaTreeGenerator(qname); return found != null ? found : findInferredGenerator(qname); } + final @Nullable AbstractAugmentGenerator findAugmentForGenerator(final QName qname) { + for (var augment : augments) { + final var gen = augment.findSchemaTreeGenerator(qname); + if (gen != null) { + return augment; + } + } + return null; + } + + final @Nullable GroupingGenerator findGroupingForGenerator(final QName qname) { + for (GroupingGenerator grouping : groupings) { + final var gen = grouping.findSchemaTreeGenerator(qname.bindTo(grouping.statement().argument().getModule())); + if (gen != null) { + return grouping; + } + } + return null; + } + private @Nullable AbstractExplicitGenerator findInferredGenerator(final QName qname) { // First search our local groupings ... - for (GroupingGenerator grouping : groupings) { - final AbstractExplicitGenerator gen = grouping.findSchemaTreeGenerator( - qname.bindTo(grouping.statement().argument().getModule())); + for (var grouping : groupings) { + final var gen = grouping.findSchemaTreeGenerator(qname.bindTo(grouping.statement().argument().getModule())); if (gen != null) { return gen; } } // ... next try local augments, which may have groupings themselves - for (AbstractAugmentGenerator augment : augments) { - final AbstractExplicitGenerator gen = augment.findSchemaTreeGenerator(qname); + for (var augment : augments) { + final var gen = augment.findSchemaTreeGenerator(qname); if (gen != null) { return gen; } @@ -250,33 +464,50 @@ abstract class AbstractCompositeGenerator> ex } } - private List createChildren(final EffectiveStatement statement) { - final List tmp = new ArrayList<>(); - final List tmpAug = new ArrayList<>(); + private Entry, List>> createChildren( + final EffectiveStatement statement) { + final var tmp = new ArrayList(); + final var tmpAug = new ArrayList(); + final var tmpSchema = new ArrayList>(); - for (EffectiveStatement stmt : statement.effectiveSubstatements()) { + for (var stmt : statement.effectiveSubstatements()) { if (stmt instanceof ActionEffectiveStatement) { - if (!isAugmenting(stmt)) { - tmp.add(new ActionGenerator((ActionEffectiveStatement) stmt, this)); + final var cast = (ActionEffectiveStatement) stmt; + if (isAugmenting(cast)) { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, ActionGenerator.class)); + } else { + tmp.add(new ActionGenerator(cast, this)); } } else if (stmt instanceof AnydataEffectiveStatement) { - if (!isAugmenting(stmt)) { - tmp.add(new OpaqueObjectGenerator<>((AnydataEffectiveStatement) stmt, this)); + final var cast = (AnydataEffectiveStatement) stmt; + if (isAugmenting(stmt)) { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, OpaqueObjectGenerator.class)); + } else { + tmp.add(new OpaqueObjectGenerator<>(cast, this)); } } else if (stmt instanceof AnyxmlEffectiveStatement) { - if (!isAugmenting(stmt)) { - tmp.add(new OpaqueObjectGenerator<>((AnyxmlEffectiveStatement) stmt, this)); + final var cast = (AnyxmlEffectiveStatement) stmt; + if (isAugmenting(stmt)) { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, OpaqueObjectGenerator.class)); + } else { + tmp.add(new OpaqueObjectGenerator<>(cast, this)); } } else if (stmt instanceof CaseEffectiveStatement) { tmp.add(new CaseGenerator((CaseEffectiveStatement) stmt, this)); } else if (stmt instanceof ChoiceEffectiveStatement) { + final var cast = (ChoiceEffectiveStatement) stmt; // FIXME: use isOriginalDeclaration() ? - if (!isAddedByUses(stmt)) { - tmp.add(new ChoiceGenerator((ChoiceEffectiveStatement) stmt, this)); + if (isAddedByUses(stmt)) { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, ChoiceGenerator.class)); + } else { + tmp.add(new ChoiceGenerator(cast, this)); } } else if (stmt instanceof ContainerEffectiveStatement) { + final var cast = (ContainerEffectiveStatement) stmt; if (isOriginalDeclaration(stmt)) { tmp.add(new ContainerGenerator((ContainerEffectiveStatement) stmt, this)); + } else { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, ContainerGenerator.class)); } } else if (stmt instanceof GroupingEffectiveStatement) { tmp.add(new GroupingGenerator((GroupingEffectiveStatement) stmt, this)); @@ -287,26 +518,38 @@ abstract class AbstractCompositeGenerator> ex tmp.add(this instanceof RpcGenerator ? new RpcContainerGenerator((InputEffectiveStatement) stmt, this) : new OperationContainerGenerator((InputEffectiveStatement) stmt, this)); } else if (stmt instanceof LeafEffectiveStatement) { - if (!isAugmenting(stmt)) { - tmp.add(new LeafGenerator((LeafEffectiveStatement) stmt, this)); + final var cast = (LeafEffectiveStatement) stmt; + if (isAugmenting(stmt)) { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, LeafGenerator.class)); + } else { + tmp.add(new LeafGenerator(cast, this)); } } else if (stmt instanceof LeafListEffectiveStatement) { - if (!isAugmenting(stmt)) { + final var cast = (LeafListEffectiveStatement) stmt; + if (isAugmenting(stmt)) { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, LeafListGenerator.class)); + } else { tmp.add(new LeafListGenerator((LeafListEffectiveStatement) stmt, this)); } } else if (stmt instanceof ListEffectiveStatement) { + final var cast = (ListEffectiveStatement) stmt; if (isOriginalDeclaration(stmt)) { - final ListGenerator listGen = new ListGenerator((ListEffectiveStatement) stmt, this); + final ListGenerator listGen = new ListGenerator(cast, this); tmp.add(listGen); final KeyGenerator keyGen = listGen.keyGenerator(); if (keyGen != null) { tmp.add(keyGen); } + } else { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, ListGenerator.class)); } } else if (stmt instanceof NotificationEffectiveStatement) { - if (!isAugmenting(stmt)) { - tmp.add(new NotificationGenerator((NotificationEffectiveStatement) stmt, this)); + final var cast = (NotificationEffectiveStatement) stmt; + if (isAugmenting(stmt)) { + tmpSchema.add(new SchemaTreePlaceholder<>(cast, NotificationGenerator.class)); + } else { + tmp.add(new NotificationGenerator(cast, this)); } } else if (stmt instanceof OutputEffectiveStatement) { // FIXME: do not generate legacy RPC layout @@ -317,6 +560,20 @@ abstract class AbstractCompositeGenerator> ex } else if (stmt instanceof TypedefEffectiveStatement) { tmp.add(new TypedefGenerator((TypedefEffectiveStatement) stmt, this)); } else if (stmt instanceof AugmentEffectiveStatement) { + // FIXME: MDSAL-695: So here we are ignoring any augment which is not in a module, while the 'uses' + // processing takes care of the rest. There are two problems here: + // + // 1) this could be an augment introduced through uses -- in this case we are picking + // confusing it with this being its declaration site, we should probably be + // ignoring it, but then + // + // 2) we are losing track of AugmentEffectiveStatement for which we do not generate + // interfaces -- and recover it at runtime through explicit walk along the + // corresponding AugmentationSchemaNode.getOriginalDefinition() pointer + // + // So here is where we should decide how to handle this augment, and make sure we + // retain information about this being an alias. That will serve as the base for keys + // in the augment -> original map we provide to BindingRuntimeTypes. if (this instanceof ModuleGenerator) { tmpAug.add(new ModuleAugmentGenerator((AugmentEffectiveStatement) stmt, this)); } @@ -324,17 +581,25 @@ abstract class AbstractCompositeGenerator> ex final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt; for (EffectiveStatement usesSub : uses.effectiveSubstatements()) { if (usesSub instanceof AugmentEffectiveStatement) { - tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, this, uses)); + tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, uses, this)); } } } else { LOG.trace("Ignoring statement {}", stmt); - continue; + } + } + + // Add any SchemaTreeChild generators to the list + for (var child : tmp) { + if (child instanceof SchemaTreeChild) { + tmpSchema.add((SchemaTreeChild) child); } } // Sort augments and add them last. This ensures child iteration order always reflects potential - // interdependencies, hence we do not need to worry about them. + // interdependencies, hence we do not need to worry about them. This is extremely important, as there are a + // number of places where we would have to either move the logic to parent statement and explicitly filter/sort + // substatements to establish this order. tmpAug.sort(AbstractAugmentGenerator.COMPARATOR); tmp.addAll(tmpAug); @@ -359,7 +624,7 @@ abstract class AbstractCompositeGenerator> ex } } - return List.copyOf(tmp); + return Map.entry(List.copyOf(tmp), List.copyOf(tmpSchema)); } // Utility equivalent of (!isAddedByUses(stmt) && !isAugmenting(stmt)). Takes advantage of relationship between