Improve OriginalLink safety
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / AbstractExplicitGenerator.java
index 9a59a88f1459c671ec8d6e8e4773b9050b4b320b..10cac0c71c5d2b669934ae1e86689a40bb15acb0 100644 (file)
@@ -24,12 +24,10 @@ import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuil
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.yang.common.AbstractQName;
 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;
 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,12 +41,20 @@ public abstract class AbstractExplicitGenerator<T extends EffectiveStatement<?,
 
     private final @NonNull T statement;
 
-    // FIXME: this, along with AbstractTypeObjectGenerator's (and TypedefGenerator's) fields should be better-controlled
-    //        with explicit sequencing guards. It it currently stands, we are expending two (or more) additional fields
-    //        to express downstream linking. If we had the concept of resolution step (an enum), we could just get by
-    //        with a simple queue of Step/Callback pairs, which would trigger as needed. For an example see how
-    //        AbstractTypeObjectGenerator manages baseGen/inferred fields.
-    private AbstractExplicitGenerator<?> prev;
+    /**
+     * Field tracking previous incarnation (along reverse of 'uses' and 'augment' axis) of this statement. This field
+     * can either be one of:
+     * <ul>
+     *   <li>{@code null} when not resolved, i.e. access is not legal, or</li>
+     *   <li>{@code this} object if this is the original definition, or</li>
+     *   <li>a generator which is one step closer to the original definition</li>
+     * </ul>
+     */
+    private AbstractExplicitGenerator<T> prev;
+    /**
+     * Field holding the original incarnation, i.e. the terminal node along {@link #prev} links.
+     */
+    private AbstractExplicitGenerator<T> orig;
 
     AbstractExplicitGenerator(final T statement) {
         this.statement = requireNonNull(statement);
@@ -78,23 +84,95 @@ public abstract class AbstractExplicitGenerator<T extends EffectiveStatement<?,
         return statement instanceof CopyableNode && ((CopyableNode) statement).isAugmenting();
     }
 
-    final void linkOriginalGenerator(final GeneratorContext context) {
-        if (isAddedByUses() || isAugmenting()) {
+    /**
+     * Attempt to link the generator corresponding to the original definition for this generator.
+     *
+     * @return {@code true} if this generator is linked
+     */
+    final boolean linkOriginalGenerator() {
+        if (orig != null) {
+            // Original already linked
+            return true;
+        }
+
+        if (prev == null) {
             LOG.trace("Linking {}", this);
-            prev = getParent().getOriginalChild(getQName());
-            LOG.trace("Linked {} to {}", this, prev);
+
+            if (!isAddedByUses() && !isAugmenting()) {
+                orig = prev = this;
+                LOG.trace("Linked {} to self", this);
+                return true;
+            }
+
+            final var link = getParent().<T>originalChild(getQName());
+            if (link == null) {
+                LOG.trace("Cannot link {} yet", this);
+                return false;
+            }
+
+            prev = link.previous();
+            orig = link.original();
+            if (orig != null) {
+                LOG.trace("Linked {} to {} original {}", this, prev, orig);
+                return true;
+            }
+
+            LOG.trace("Linked {} to intermediate {}", this, prev);
+            return false;
         }
+
+        orig = prev.originalLink().original();
+        if (orig != null) {
+            LOG.trace("Linked {} to original {}", this, orig);
+            return true;
+        }
+        return false;
     }
 
-    final @Nullable AbstractExplicitGenerator<?> previous() {
-        return prev;
+    /**
+     * Return the previous incarnation of this generator, or {@code null} if this is the original generator.
+     *
+     * @return Previous incarnation or {@code null}
+     */
+    final @Nullable AbstractExplicitGenerator<T> previous() {
+        final var local = verifyNotNull(prev, "Generator %s does not have linkage to previous instance resolved", this);
+        return local == this ? null : local;
     }
 
-    @NonNull AbstractExplicitGenerator<?> getOriginal() {
-        return prev == null ? this : prev.getOriginal();
+    /**
+     * Return the original incarnation of this generator, or self if this is the original generator.
+     *
+     * @return Original incarnation of this generator
+     */
+    @NonNull AbstractExplicitGenerator<T> getOriginal() {
+        return verifyNotNull(orig, "Generator %s does not have linkage to original instance resolved", this);
+    }
+
+    @Nullable AbstractExplicitGenerator<T> tryOriginal() {
+        return orig;
+    }
+
+    /**
+     * Return the link towards the original generator.
+     *
+     * @return Link towards the original generator.
+     */
+    final @NonNull OriginalLink<T> originalLink() {
+        final var local = prev;
+        if (local == null) {
+            return OriginalLink.partial(this);
+        } else if (local == this) {
+            return OriginalLink.complete(this);
+        } else {
+            return OriginalLink.partial(local);
+        }
     }
 
     @Nullable AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
+        return findLocalSchemaTreeGenerator(qname);
+    }
+
+    final @Nullable AbstractExplicitGenerator<?> findLocalSchemaTreeGenerator(final QName qname) {
         for (Generator child : this) {
             if (child instanceof AbstractExplicitGenerator) {
                 final AbstractExplicitGenerator<?> gen = (AbstractExplicitGenerator<?>) child;
@@ -107,27 +185,6 @@ public abstract class AbstractExplicitGenerator<T extends EffectiveStatement<?,
         return null;
     }
 
-    final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final @NonNull SchemaNodeIdentifier path,
-            final @Nullable QNameModule targetModule) {
-        AbstractExplicitGenerator<?> current = this;
-        QNameModule currentModule = targetModule;
-
-        for (QName next : path.getNodeIdentifiers()) {
-            final QName qname = currentModule == null ? next : next.bindTo(currentModule);
-            current = verifyNotNull(current.findSchemaTreeGenerator(qname),
-                "Failed to find %s as %s in %s", next, qname, current);
-
-            final QNameModule foundNamespace = current.getQName().getModule();
-            if (!foundNamespace.equals(qname.getModule())) {
-                // We have located a different QName than the one we were looking for. We need to make sure we adjust
-                // all subsequent QNames to this new namespace
-                currentModule = foundNamespace;
-            }
-        }
-
-        return current;
-    }
-
     final @NonNull QName getQName() {
         final Object arg = statement.argument();
         verify(arg instanceof QName, "Unexpected argument %s", arg);