Add StmtContext.findFirstSubstatementArgument()
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / InferredStatementContext.java
index 5c5ed98e47eb9c2271c003bb6b1845736f0a2634..88e491e265bffc8893c17b92ccee9a63b78e8b0d 100644 (file)
@@ -17,17 +17,20 @@ import java.util.List;
 import java.util.Optional;
 import java.util.stream.Stream;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
-import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.OnDemandSchemaTreeStorageNode;
 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextDefaults;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,7 +41,7 @@ import org.slf4j.LoggerFactory;
  * effective substatements, which are either transformed from that prototype or added by inference.
  */
 final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
-        extends StatementContextBase<A, D, E> {
+        extends StatementContextBase<A, D, E> implements OnDemandSchemaTreeStorageNode {
     private static final Logger LOG = LoggerFactory.getLogger(InferredStatementContext.class);
 
     private final @NonNull StatementContextBase<A, D, E> prototype;
@@ -57,6 +60,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         this.prototype = original.prototype;
         this.originalCtx = original.originalCtx;
         this.argument = original.argument;
+        setSubstatementsInitialized();
     }
 
     InferredStatementContext(final StatementContextBase<?, ?, ?> parent, final StatementContextBase<A, D, E> prototype,
@@ -70,8 +74,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         this.targetModule = targetModule;
         this.originalCtx = prototype.getOriginalCtx().orElse(prototype);
 
-        // FIXME: YANGTOOLS-784: instantiate these lazily
-        addEffectiveSubstatements(createEffective());
+        // Note: substatements from prototype are initialized lazily through ensureSubstatements()
     }
 
     @Override
@@ -120,14 +123,99 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         return originalCtx.buildDeclared();
     }
 
+    @Override
+    public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
+        ensureSubstatements();
+        return super.mutableEffectiveSubstatements();
+    }
+
+    @Override
+    public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
+        ensureSubstatements();
+        super.addEffectiveSubstatement(substatement);
+    }
+
+    @Override
+    public void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
+        ensureSubstatements();
+        super.addEffectiveSubstatements(statements);
+    }
+
+    @Override
+    public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
+            final String statementArg) {
+        ensureSubstatements();
+        super.removeStatementFromEffectiveSubstatements(statementDef, statementArg);
+    }
+
+    @Override
+    public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
+        ensureSubstatements();
+        super.removeStatementFromEffectiveSubstatements(statementDef);
+    }
+
     @Override
     InferredStatementContext<A, D, E> reparent(final StatementContextBase<?, ?, ?> newParent) {
         return new InferredStatementContext<>(this, newParent);
     }
 
+    @Override
+    boolean hasEmptySubstatements() {
+        ensureSubstatements();
+        return hasEmptyEffectiveSubstatements();
+    }
+
+    @Override
+    public <X, Z extends EffectiveStatement<X, ?>> @NonNull Optional<X> findSubstatementArgument(
+            final @NonNull Class<Z> type) {
+        if (substatementsInitialized()) {
+            return StmtContextDefaults.findSubstatementArgument(this, type);
+        }
+
+        final Optional<X> templateArg = prototype.findSubstatementArgument(type);
+        if (templateArg.isEmpty()) {
+            return templateArg;
+        }
+        if (SchemaTreeEffectiveStatement.class.isAssignableFrom(type)) {
+            // X is known to be QName
+            return (Optional<X>) templateArg.map(template -> ((QName) template).bindTo(targetModule));
+        }
+        return templateArg;
+    }
+
+    @Override
+    public boolean hasSubstatement(final @NonNull Class<? extends EffectiveStatement<?, ?>> type) {
+        return substatementsInitialized() ? StmtContextDefaults.hasSubstatement(prototype, type)
+            : prototype.hasSubstatement(type);
+    }
+
+    @Override
+    public <D extends DeclaredStatement<QName>, E extends EffectiveStatement<QName, D>>
+            StmtContext<QName, D, E> requestSchemaTreeChild(final QName qname) {
+        LOG.debug("Materializing on lookup of {}", qname);
+        // FIXME: YANGTOOLS-1160: we do not want to force full materialization here
+        ensureSubstatements();
+
+        // Now we have to do a lookup as we do not have access to the namespace being populated (yet). Here we are
+        // bypassing additional checks and talk directly to superclass to get the statements.
+        for (StmtContext<?, ?, ?> stmt : super.mutableEffectiveSubstatements()) {
+            if (stmt.producesEffective(SchemaTreeEffectiveStatement.class)
+                && qname.equals(stmt.coerceStatementArgument())) {
+                return (StmtContext<QName, D, E>) stmt;
+            }
+        }
+        return null;
+    }
+
     // Instantiate this statement's effective substatements. Note this method has side-effects in namespaces and overall
     // BuildGlobalContext, hence it must be called at most once.
-    private List<Mutable<?, ?, ?>> createEffective() {
+    private void ensureSubstatements() {
+        if (!substatementsInitialized()) {
+            initializeSubstatements();
+        }
+    }
+
+    private void initializeSubstatements() {
         final Collection<? extends StatementContextBase<?, ?, ?>> declared = prototype.mutableDeclaredSubstatements();
         final Collection<? extends Mutable<?, ?, ?>> effective = prototype.mutableEffectiveSubstatements();
         final List<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
@@ -141,7 +229,9 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
             copySubstatement(stmtContext, buffer);
         }
 
-        return buffer;
+        // We are bypassing usual safeties here, as this is not introducing new statements but rather just materializing
+        // them when the need has arised.
+        addInitialEffectiveSubstatements(buffer);
     }
 
     // Statement copy mess starts here
@@ -149,15 +239,11 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
     // FIXME: This is messy and is probably wrong in some corner case. Even if it is correct, the way how it is correct
     //        relies on hard-coded maps. At the end of the day, the logic needs to be controlled by statement's
     //        StatementSupport.
-    // FIXME: YANGTOOLS-652: these maps look very much like those in UsesStatementImpl
+    // FIXME: YANGTOOLS-652: this map looks very much like UsesStatementSupport.TOP_REUSED_DEF_SET
     private static final ImmutableSet<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
         YangStmtMapping.TYPE,
         YangStmtMapping.TYPEDEF,
         YangStmtMapping.USES);
-    private static final ImmutableSet<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
-        YangStmtMapping.DESCRIPTION,
-        YangStmtMapping.REFERENCE,
-        YangStmtMapping.STATUS);
 
     private void copySubstatement(final Mutable<?, ?, ?> substatement, final Collection<Mutable<?, ?, ?>> buffer) {
         final StatementDefinition def = substatement.getPublicDefinition();
@@ -168,22 +254,8 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
             buffer.add(substatement);
             return;
         }
-        // FIXME: YANGTOOLS-652: formerly known as "needToCopyByUses" (note inverted check, though)
-        if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
-            // This is to say: if parent of source context is a grouping, ignore this statement.
-            if (YangStmtMapping.GROUPING.equals(substatement.coerceParentContext().getPublicDefinition())) {
-                LOG.debug("Skipping grouping statement {}", substatement);
-                return;
-            }
-        }
 
-        // FIXME: YANGTOOLS-694: we are forcing a copy here, hence even statements not affected by parent, copyType
-        //                       or targetModule (and don't forget its substatements!). This really should be a callout
-        //                       to StatementSupport. Note if that callout is allowed to return an Optional, it can
-        //                       take care at least of the 'grouping from uses' case above.
-        final Mutable<?, ?, ?> copy = childCopyOf(substatement, childCopyType, targetModule);
-        LOG.debug("Copying substatement {} for {} as {}", substatement, this, copy);
-        buffer.add(copy);
+        substatement.copyAsChildOf(this, childCopyType, targetModule).ifPresent(buffer::add);
     }
 
     // Statement copy mess ends here
@@ -195,6 +267,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
      * properly updated there.
      */
     @Override
+    @Deprecated
     public Optional<SchemaPath> getSchemaPath() {
         return substatementGetSchemaPath();
     }
@@ -215,7 +288,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
     }
 
     @Override
-    public NamespaceStorageNode getParentNamespaceStorage() {
+    public StatementContextBase<?, ?, ?> getParentNamespaceStorage() {
         return parent;
     }