Remove a naked cast
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / InferredStatementContext.java
index b1693ae1d592d3090b0a45bcca01f4420f24506b..ba888efc4b9131916ed9486c2da2d14481d1d154 100644 (file)
@@ -12,6 +12,7 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.VerifyException;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Streams;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -40,6 +41,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
 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.StatementFactory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
@@ -87,7 +89,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
 
     private final @NonNull StatementContextBase<A, D, E> prototype;
     private final @NonNull StatementContextBase<?, ?, ?> parent;
-    private final @NonNull StmtContext<A, D, E> originalCtx;
+    private final @NonNull ReactorStmtCtx<A, D, E> originalCtx;
     private final QNameModule targetModule;
     private final A argument;
 
@@ -122,7 +124,10 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         this.argument = targetModule == null ? prototype.argument()
                 : prototype.definition().adaptArgumentValue(prototype, targetModule);
         this.targetModule = targetModule;
-        this.originalCtx = prototype.getOriginalCtx().orElse(prototype);
+
+        final var origCtx = prototype.getOriginalCtx().orElse(prototype);
+        verify(origCtx instanceof ReactorStmtCtx, "Unexpected original %s", origCtx);
+        this.originalCtx = (ReactorStmtCtx<A, D, E>) origCtx;
 
         // Mark prototype as blocking statement cleanup
         prototype.incRef();
@@ -212,18 +217,23 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         // If we have not materialized we do not have a difference in effective substatements, hence we can forward
         // towards the source of the statement.
         accessSubstatements();
-        return substatements == null ? tryToReusePrototype(factory) : createInferredEffective(factory, this);
+        return substatements == null ? tryToReusePrototype(factory) : createInferredEffective(factory);
+    }
+
+    private @NonNull E createInferredEffective(final @NonNull StatementFactory<A, D, E> factory) {
+        return createInferredEffective(factory, this, streamDeclared(), streamEffective());
     }
 
     @Override
-    E createInferredEffective(final StatementFactory<A, D, E> factory, final InferredStatementContext<A, D, E> ctx) {
-        return prototype.createInferredEffective(factory, ctx);
+    E createInferredEffective(final StatementFactory<A, D, E> factory, final InferredStatementContext<A, D, E> ctx,
+            final Stream<? extends ReactorStmtCtx<?, ?, ?>> declared,
+            final Stream<? extends ReactorStmtCtx<?, ?, ?>> effective) {
+        return originalCtx.createInferredEffective(factory, ctx, declared, effective);
     }
 
     private @NonNull E tryToReusePrototype(final @NonNull StatementFactory<A, D, E> factory) {
-        final E origEffective = prototype.buildEffective();
-        final Collection<? extends @NonNull EffectiveStatement<?, ?>> origSubstatements =
-            origEffective.effectiveSubstatements();
+        final var origEffective = prototype.buildEffective();
+        final var origSubstatements = origEffective.effectiveSubstatements();
 
         // First check if we can reuse the entire prototype
         if (!factory.canReuseCurrent(this, prototype, origSubstatements)) {
@@ -248,14 +258,8 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         }
 
         // ... copy-sensitive check
-        final List<EffectiveCopy> declCopy = prototype.streamDeclared()
-            .map(sub -> effectiveCopy((ReactorStmtCtx<?, ?, ?>) sub))
-            .filter(Objects::nonNull)
-            .collect(Collectors.toUnmodifiableList());
-        final List<EffectiveCopy> effCopy = prototype.streamEffective()
-            .map(sub -> effectiveCopy((ReactorStmtCtx<?, ?, ?>) sub))
-            .filter(Objects::nonNull)
-            .collect(Collectors.toUnmodifiableList());
+        final var declCopy = effectiveCopy(prototype.streamDeclared());
+        final var effCopy = effectiveCopy(prototype.streamEffective());
 
         // ... are any copy-sensitive?
         if (allReused(declCopy) && allReused(effCopy)) {
@@ -268,18 +272,15 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         }
 
         // *sigh*, ok, heavy lifting through a shallow copy
-        final List<ReactorStmtCtx<?, ?, ?>> declared = declCopy.stream()
-            .map(copy -> copy.toChildContext(this))
-            .collect(ImmutableList.toImmutableList());
-        final List<ReactorStmtCtx<?, ?, ?>> effective = effCopy.stream()
-            .map(copy -> copy.toChildContext(this))
-            .collect(ImmutableList.toImmutableList());
+        final var declared = adoptSubstatements(declCopy);
+        final var effective = adoptSubstatements(effCopy);
         substatements = declared.isEmpty() ? effective
             : Streams.concat(declared.stream(), effective.stream()).collect(ImmutableList.toImmutableList());
         prototype.decRef();
 
         // Values are the effective copies, hence this efficiently deals with recursion.
-        return internAlongCopyAxis(factory, factory.createEffective(this, declared.stream(), effective.stream()));
+        return internAlongCopyAxis(factory,
+            originalCtx.createInferredEffective(factory, this, declared.stream(), effective.stream()));
     }
 
     private @NonNull E tryToReuseSubstatements(final @NonNull StatementFactory<A, D, E> factory,
@@ -293,7 +294,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
 
         // Fall back to full instantiation, which populates our substatements. Then check if we should be reusing
         // the substatement list, as this operation turned out to not affect them.
-        final E effective = createInferredEffective(factory, this);
+        final E effective = createInferredEffective(factory);
         // Since we have forced instantiation to deal with this case, we also need to reset the 'modified' flag
         setUnmodified();
 
@@ -318,10 +319,10 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         return reusePrototypeReplicas(Streams.concat(prototype.streamDeclared(), prototype.streamEffective()));
     }
 
-    private List<ReactorStmtCtx<?, ?, ?>> reusePrototypeReplicas(final Stream<StmtContext<?, ?, ?>> stream) {
+    private List<ReactorStmtCtx<?, ?, ?>> reusePrototypeReplicas(final Stream<ReactorStmtCtx<?, ?, ?>> stream) {
         return stream
             .map(stmt -> {
-                final ReplicaStatementContext<?, ?, ?> ret = ((ReactorStmtCtx<?, ?, ?>) stmt).replicaAsChildOf(this);
+                final var ret = stmt.replicaAsChildOf(this);
                 ret.buildEffective();
                 return ret;
             })
@@ -453,8 +454,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         final Mutable<QName, Y, Z> ret = (Mutable<QName, Y, Z>) copySubstatement((Mutable<?, ?, ?>) template)
             .orElseThrow(
                 () -> new InferenceException(this, "Failed to materialize child %s template %s", qname, template));
-        ensureCompletedPhase(ret);
-        addMaterialized(template, ret);
+        addMaterialized(template, ensureCompletedPhase(ret));
 
         LOG.debug("Child {} materialized", qname);
         return ret;
@@ -465,7 +465,8 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
     private List<ReactorStmtCtx<?, ?, ?>> ensureEffectiveSubstatements() {
         accessSubstatements();
         return substatements instanceof List ? castEffective(substatements)
-            : initializeSubstatements(castMaterialized(substatements));
+            // We have either not started or have only partially-materialized statements, ensure full materialization
+            : initializeSubstatements();
     }
 
     @Override
@@ -484,12 +485,12 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
     }
 
     @Override
-    Stream<? extends @NonNull StmtContext<?, ?, ?>> streamDeclared() {
+    Stream<? extends @NonNull ReactorStmtCtx<?, ?, ?>> streamDeclared() {
         return Stream.empty();
     }
 
     @Override
-    Stream<? extends @NonNull StmtContext<?, ?, ?>> streamEffective() {
+    Stream<? extends @NonNull ReactorStmtCtx<?, ?, ?>> streamEffective() {
         return ensureEffectiveSubstatements().stream().filter(StmtContext::isSupportedToBuildEffective);
     }
 
@@ -520,23 +521,32 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         return count;
     }
 
-    private List<ReactorStmtCtx<?, ?, ?>> initializeSubstatements(
-            final Map<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>> materializedSchemaTree) {
-        final Collection<? extends StatementContextBase<?, ?, ?>> declared = prototype.mutableDeclaredSubstatements();
-        final Collection<? extends Mutable<?, ?, ?>> effective = prototype.mutableEffectiveSubstatements();
+    private List<ReactorStmtCtx<?, ?, ?>> initializeSubstatements() {
+        final var declared = prototype.mutableDeclaredSubstatements();
+        final var effective = prototype.mutableEffectiveSubstatements();
+
+        // We are about to instantiate some substatements. The simple act of materializing them may end up triggering
+        // namespace lookups, which in turn can materialize copies by themselves, running ahead of our materialization.
+        // We therefore need a meeting place for, which are the partially-materialized substatements. If we do not have
+        // them yet, instantiate them and we need to populate them as well.
+        final int expectedSize = declared.size() + effective.size();
+        var materializedSchemaTree = castMaterialized(substatements);
+        if (materializedSchemaTree == null) {
+            substatements = materializedSchemaTree = Maps.newHashMapWithExpectedSize(expectedSize);
+        }
 
-        final List<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
-        for (final Mutable<?, ?, ?> stmtContext : declared) {
+        final var buffer = new ArrayList<ReactorStmtCtx<?, ?, ?>>(expectedSize);
+        for (var stmtContext : declared) {
             if (stmtContext.isSupportedByFeatures()) {
                 copySubstatement(stmtContext, buffer, materializedSchemaTree);
             }
         }
-        for (final Mutable<?, ?, ?> stmtContext : effective) {
+        for (var stmtContext : effective) {
             copySubstatement(stmtContext, buffer, materializedSchemaTree);
         }
 
-        final List<ReactorStmtCtx<?, ?, ?>> ret = beforeAddEffectiveStatementUnsafe(ImmutableList.of(), buffer.size());
-        ret.addAll((Collection) buffer);
+        final var ret = beforeAddEffectiveStatementUnsafe(ImmutableList.of(), buffer.size());
+        ret.addAll(buffer);
         substatements = ret;
         setModified();
 
@@ -549,23 +559,56 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
     // sometimes. Tread softly because you tread on my dreams.
     //
 
-    private EffectiveCopy effectiveCopy(final ReactorStmtCtx<?, ?, ?> stmt) {
-        final ReactorStmtCtx<?, ?, ?> effective = stmt.asEffectiveChildOf(this, childCopyType(), targetModule);
-        return effective == null ? null : new EffectiveCopy(stmt, effective);
+    private ImmutableList<ReactorStmtCtx<?, ?, ?>> adoptSubstatements(final List<EffectiveCopy> list) {
+        return list.stream()
+            .map(copy -> copy.toChildContext(this))
+            .collect(ImmutableList.toImmutableList());
     }
 
-    private void copySubstatement(final Mutable<?, ?, ?> substatement, final Collection<Mutable<?, ?, ?>> buffer,
+    private List<EffectiveCopy> effectiveCopy(final Stream<? extends ReactorStmtCtx<?, ?, ?>> stream) {
+        return stream
+            .map(this::effectiveCopy)
+            .filter(Objects::nonNull)
+            .collect(Collectors.toUnmodifiableList());
+    }
+
+    /**
+     * Create an effective copy of a prototype's substatement as a child of this statement. This is a bit tricky, as
+     * we are called from {@link #tryToReusePrototype(StatementFactory)} and we are creating copies of prototype
+     * statements -- which triggers {@link StatementSupport#onStatementAdded(Mutable)}, which in turn can loop around
+     * to {@link #requestSchemaTreeChild(QName)} -- which creates the statement and hence we can end up performing two
+     * copies.
+     *
+     * @param template Prototype substatement
+     * @return An {@link EffectiveCopy}, or {@code null} if not applicable
+     */
+    private @Nullable EffectiveCopy effectiveCopy(final ReactorStmtCtx<?, ?, ?> template) {
+        if (substatements instanceof HashMap) {
+            // we have partial materialization by requestSchemaTreeChild() after we started tryToReusePrototype(), check
+            // if the statement has already been copied -- we need to pick it up in that case.
+            final var copy = castMaterialized(substatements).get(template);
+            if (copy != null) {
+                return new EffectiveCopy(template, copy);
+            }
+        }
+
+        final var copy = template.asEffectiveChildOf(this, childCopyType(), targetModule);
+        return copy == null ? null : new EffectiveCopy(template, copy);
+    }
+
+    private void copySubstatement(final Mutable<?, ?, ?> substatement, final Collection<ReactorStmtCtx<?, ?, ?>> buffer,
             final Map<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>> materializedSchemaTree) {
         // Consult materialized substatements. We are in a copy operation and will end up throwing materialized
         // statements away -- hence we do not perform Map.remove() to save ourselves a mutation operation.
         //
         // We could also perform a Map.containsKey() and perform a bulk add, but that would mean the statement order
         // against parent would change -- and we certainly do not want that to happen.
-        final ReactorStmtCtx<?, ?, ?> materialized = findMaterialized(materializedSchemaTree, substatement);
+        final var materialized = findMaterialized(materializedSchemaTree, substatement);
         if (materialized == null) {
             copySubstatement(substatement).ifPresent(copy -> {
-                ensureCompletedPhase(copy);
-                buffer.add(copy);
+                final var cast = ensureCompletedPhase(copy);
+                materializedSchemaTree.put(substatement, cast);
+                buffer.add(cast);
             });
         } else {
             buffer.add(materialized);
@@ -576,7 +619,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         return substatement.copyAsChildOf(this, childCopyType(), targetModule);
     }
 
-    private void addMaterialized(final StmtContext<?, ?, ?> template, final Mutable<?, ?, ?> copy) {
+    private void addMaterialized(final StmtContext<?, ?, ?> template, final ReactorStmtCtx<?, ?, ?> copy) {
         final HashMap<StmtContext<?, ?, ?>, ReactorStmtCtx<?, ?, ?>> materializedSchemaTree;
         if (substatements == null) {
             // Lazy initialization of backing map. We do not expect this to be used often or multiple times -- each hit
@@ -591,8 +634,7 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
             materializedSchemaTree = castMaterialized(substatements);
         }
 
-        final StmtContext<?, ?, ?> existing = materializedSchemaTree.put(template,
-            (StatementContextBase<?, ?, ?>) copy);
+        final var existing = materializedSchemaTree.put(template, copy);
         if (existing != null) {
             throw new VerifyException(
                 "Unexpected duplicate request for " + copy.argument() + " previous result was " + existing);
@@ -663,6 +705,19 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         return isIgnoringConfig(parent);
     }
 
+    @Override
+    public boolean isSupportedToBuildEffective() {
+        // Our prototype may have fizzled, for example due to it being a implicit statement guarded by if-feature which
+        // evaluates to false. If that happens, this statement also needs to report unsupported -- and we want to cache
+        // that information for future reuse.
+        boolean ret = super.isSupportedToBuildEffective();
+        if (ret && !prototype.isSupportedToBuildEffective()) {
+            setUnsupported();
+            ret = false;
+        }
+        return ret;
+    }
+
     @Override
     protected boolean isParentSupportedByFeatures() {
         return parent.isSupportedByFeatures();