Remove a naked cast
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / InferredStatementContext.java
index f5a00a14991712bb0e2bf6001c6be3ed561458e4..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;
@@ -224,15 +226,14 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
 
     @Override
     E createInferredEffective(final StatementFactory<A, D, E> factory, final InferredStatementContext<A, D, E> ctx,
-            final Stream<? extends StmtContext<?, ?, ?>> declared,
-            final Stream<? extends StmtContext<?, ?, ?>> effective) {
+            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)) {
@@ -257,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)) {
@@ -277,12 +272,8 @@ 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();
@@ -328,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;
             })
@@ -474,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
@@ -493,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);
     }
 
@@ -529,22 +521,31 @@ 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 var buffer = new ArrayList<ReactorStmtCtx<?, ?, ?>>(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());
+        final var ret = beforeAddEffectiveStatementUnsafe(ImmutableList.of(), buffer.size());
         ret.addAll(buffer);
         substatements = ret;
         setModified();
@@ -558,9 +559,41 @@ 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 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,
@@ -570,10 +603,12 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         //
         // 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 -> {
-                buffer.add(ensureCompletedPhase(copy));
+                final var cast = ensureCompletedPhase(copy);
+                materializedSchemaTree.put(substatement, cast);
+                buffer.add(cast);
             });
         } else {
             buffer.add(materialized);
@@ -670,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();