Simplify original tracking
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / AbstractCompositeGenerator.java
index 2ea6f558a4d09759ed070923c4581005046eeffa..6dae9ac58423207f2d426e9f13d6523fbccda3d2 100644 (file)
@@ -23,6 +23,7 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
 import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
@@ -42,6 +43,8 @@ import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.ri.type.TypeBuilder;
@@ -62,6 +65,10 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
     private List<AbstractAugmentGenerator> augments = List.of();
     private List<GroupingGenerator> groupings;
 
+    // Performance optimization: if this is true, we have ascertained our original definition as well that of all our
+    // children
+    private boolean originalsResolved;
+
     AbstractCompositeGenerator(final T statement) {
         super(statement);
         children = createChildren(statement);
@@ -82,11 +89,53 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
         return children.isEmpty();
     }
 
-    @Override
-    final @Nullable Generator findGenerator(final EffectiveStatement<?, ?> stmt) {
-        for (Generator gen : children) {
-            if (gen instanceof AbstractExplicitGenerator && ((AbstractExplicitGenerator<?>) gen).statement() == stmt) {
-                return gen;
+    final @Nullable AbstractExplicitGenerator<?> findGenerator(final List<EffectiveStatement<?, ?>> stmtPath) {
+        return findGenerator(MatchStrategy.identity(), stmtPath, 0);
+    }
+
+    final @Nullable AbstractExplicitGenerator<?> findGenerator(final MatchStrategy childStrategy,
+            // TODO: Wouldn't this method be nicer with Deque<EffectiveStatement<?, ?>> ?
+            final List<EffectiveStatement<?, ?>> stmtPath, final int offset) {
+        final EffectiveStatement<?, ?> stmt = stmtPath.get(offset);
+
+        // Try direct children first, which is simple
+        AbstractExplicitGenerator<?> ret = childStrategy.findGenerator(stmt, children);
+        if (ret != null) {
+            final int next = offset + 1;
+            if (stmtPath.size() == next) {
+                // Final step, return child
+                return ret;
+            }
+            if (ret instanceof AbstractCompositeGenerator) {
+                // We know how to descend down
+                return ((AbstractCompositeGenerator<?>) ret).findGenerator(childStrategy, stmtPath, next);
+            }
+            // Yeah, don't know how to continue here
+            return null;
+        }
+
+        // At this point we are about to fork for augments or groupings. In either case only schema tree statements can
+        // be found this way. The fun part is that if we find a match and need to continue, we will use the same
+        // strategy for children as well. We now know that this (and subsequent) statements need to have a QName
+        // argument.
+        if (stmt instanceof SchemaTreeEffectiveStatement) {
+            // grouping -> uses instantiation changes the namespace to the local namespace of the uses site. We are
+            // going the opposite direction, hence we are changing namespace from local to the grouping's namespace.
+            for (GroupingGenerator gen : groupings) {
+                final MatchStrategy strat = MatchStrategy.grouping(gen);
+                ret = gen.findGenerator(strat, stmtPath, offset);
+                if (ret != null) {
+                    return ret;
+                }
+            }
+
+            // All augments are dead simple: they need to match on argument (which we expect to be a QName)
+            final MatchStrategy strat = MatchStrategy.augment();
+            for (AbstractAugmentGenerator gen : augments) {
+                ret = gen.findGenerator(strat, stmtPath, offset);
+                if (ret != null) {
+                    return ret;
+                }
             }
         }
         return null;
@@ -97,21 +146,11 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> 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 are resolving 'uses' statements to their corresponding 'grouping' definitions
         final List<GroupingGenerator> tmp = new ArrayList<>();
         for (EffectiveStatement<?, ?> stmt : statement().effectiveSubstatements()) {
             if (stmt instanceof UsesEffectiveStatement) {
-                final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt;
-                final GroupingGenerator grouping = context.resolveTreeScoped(GroupingGenerator.class, uses.argument());
-                tmp.add(grouping);
-
-                for (Generator gen : this) {
-                    if (gen instanceof UsesAugmentGenerator) {
-                        ((UsesAugmentGenerator) gen).linkGroupingDependency(uses, grouping);
-                    }
-                }
+                tmp.add(context.resolveTreeScoped(GroupingGenerator.class, ((UsesEffectiveStatement) stmt).argument()));
             }
         }
         groupings = List.copyOf(tmp);
@@ -124,16 +163,36 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
         augments.add(requireNonNull(augment));
     }
 
+    @Override
+    long linkOriginalGenerator() {
+        if (originalsResolved) {
+            return 0;
+        }
+
+        long remaining = super.linkOriginalGenerator();
+        if (remaining == 0) {
+            for (Generator child : children) {
+                if (child instanceof AbstractExplicitGenerator) {
+                    remaining += ((AbstractExplicitGenerator<?>) child).linkOriginalGenerator();
+                }
+            }
+            if (remaining == 0) {
+                originalsResolved = true;
+            }
+        }
+        return remaining;
+    }
+
     @Override
     final AbstractCompositeGenerator<?> getOriginal() {
         return (AbstractCompositeGenerator<?>) super.getOriginal();
     }
 
-    final @NonNull AbstractExplicitGenerator<?> getOriginalChild(final QName childQName) {
+    final @NonNull OriginalLink getOriginalChild(final QName childQName) {
         // First try groupings/augments ...
         final AbstractExplicitGenerator<?> found = findInferredGenerator(childQName);
         if (found != null) {
-            return found;
+            return OriginalLink.partial(found);
         }
 
         // ... no luck, we really need to start looking at our origin
@@ -142,11 +201,11 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
 
         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();
+            "Failed to find child %s (proxy for %s) in %s", prevQName, childQName, prev).originalLink();
     }
 
     @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);
     }
@@ -170,6 +229,56 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
         return null;
     }
 
+    final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final SchemaNodeIdentifier path) {
+        // This is not quite straightforward. 'path' works on top of schema tree, which is instantiated view. Since we
+        // do not generate duplicate instantiations along 'uses' path, findSchemaTreeGenerator() would satisfy our
+        // request by returning a child of the source 'grouping'.
+        //
+        // When that happens, our subsequent lookups need to adjust the namespace being looked up to the grouping's
+        // namespace... except for the case when the step is actually an augmentation, in which case we must not make
+        // that adjustment.
+        //
+        // Hence we deal with this lookup recursively, dropping namespace hints when we cross into groupings.
+        return resolveSchemaNode(path.getNodeIdentifiers().iterator(), null);
+    }
+
+    private @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final Iterator<QName> qnames,
+            final @Nullable QNameModule localNamespace) {
+        final QName qname = qnames.next();
+
+        // First try local augments, as those are guaranteed to match namespace exactly
+        for (AbstractAugmentGenerator augment : augments) {
+            final AbstractExplicitGenerator<?> gen = augment.findSchemaTreeGenerator(qname);
+            if (gen != null) {
+                return resolveNext(gen, qnames, null);
+            }
+        }
+
+        // Second try local groupings, as those perform their own adjustment
+        for (GroupingGenerator grouping : groupings) {
+            final QNameModule ns = grouping.statement().argument().getModule();
+            final AbstractExplicitGenerator<?> gen = grouping.findSchemaTreeGenerator(qname.bindTo(ns));
+            if (gen != null) {
+                return resolveNext(gen, qnames, ns);
+            }
+        }
+
+        // Lastly try local statements adjusted with namespace, if applicable
+        final QName lookup = localNamespace == null ? qname : qname.bindTo(localNamespace);
+        final AbstractExplicitGenerator<?> gen = verifyNotNull(super.findSchemaTreeGenerator(lookup),
+            "Failed to find %s as %s in %s", qname, lookup, this);
+        return resolveNext(gen, qnames, localNamespace);
+    }
+
+    private static @NonNull AbstractExplicitGenerator<?> resolveNext(final @NonNull AbstractExplicitGenerator<?> gen,
+            final Iterator<QName> qnames, final QNameModule localNamespace) {
+        if (qnames.hasNext()) {
+            verify(gen instanceof AbstractCompositeGenerator, "Unexpected generator %s", gen);
+            return ((AbstractCompositeGenerator<?>) gen).resolveSchemaNode(qnames, localNamespace);
+        }
+        return gen;
+    }
+
     /**
      * Update the specified builder to implement interfaces generated for the {@code grouping} statements this generator
      * is using.
@@ -274,6 +383,20 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> 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));
                 }
@@ -281,7 +404,7 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> 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, this));
                     }
                 }
             } else {