Remove a naked cast
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / InferredStatementContext.java
index 3d85bda8420965321ac635387ac7a57463f42fd4..ba888efc4b9131916ed9486c2da2d14481d1d154 100644 (file)
@@ -41,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;
@@ -231,9 +232,8 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
     }
 
     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)) {
@@ -258,14 +258,8 @@ final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extend
         }
 
         // ... copy-sensitive check
-        final List<EffectiveCopy> declCopy = prototype.streamDeclared()
-            .map(this::effectiveCopy)
-            .filter(Objects::nonNull)
-            .collect(Collectors.toUnmodifiableList());
-        final List<EffectiveCopy> effCopy = prototype.streamEffective()
-            .map(this::effectiveCopy)
-            .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)) {
@@ -278,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();
@@ -569,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,
@@ -683,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();