Improve StatementContextBase.stream{Declared,Effective}
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / ReactorStmtCtx.java
index 5b90299c327a76c7cf4c3f71a0d6db0d96c3938f..a00b5446e56db4d2fbfd566267cc50fec0fd18af 100644 (file)
@@ -17,8 +17,10 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Stream;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.YangVersion;
@@ -32,7 +34,9 @@ import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.ParserNamespaces;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStatementState;
 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
@@ -40,12 +44,11 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.ExecutionOrder;
 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementFactory;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
-import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
-import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -117,7 +120,7 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
      * {@link #buildEffective()} instance. If this context is reused, it can be inflated to {@link EffectiveInstances}
      * and also act as a common instance reuse site.
      */
-    private @Nullable E effectiveInstance;
+    private @Nullable Object effectiveInstance;
 
     // Master flag controlling whether this context can yield an effective statement
     // FIXME: investigate the mechanics that are being supported by this, as it would be beneficial if we can get rid
@@ -189,10 +192,10 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
     public abstract RootStatementContext<?, ?, ?> getRoot();
 
     @Override
-    public abstract Collection<? extends StatementContextBase<?, ?, ?>> mutableDeclaredSubstatements();
+    public abstract Collection<? extends @NonNull StatementContextBase<?, ?, ?>> mutableDeclaredSubstatements();
 
     @Override
-    public final @NonNull Registry getBehaviourRegistry() {
+    public final Registry getBehaviourRegistry() {
         return getRoot().getBehaviourRegistryImpl();
     }
 
@@ -233,14 +236,8 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
 
     @Override
     public final QName moduleName() {
-        final RootStatementContext<?, ?, ?> root = getRoot();
-        return QName.create(StmtContextUtils.getRootModuleQName(root), root.getRawArgument());
-    }
-
-    @Override
-    @Deprecated(since = "7.0.9", forRemoval = true)
-    public final EffectiveStatement<?, ?> original() {
-        return getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+        final var root = getRoot();
+        return QName.create(StmtContextUtils.getModuleQName(root), root.getRawArgument());
     }
 
     //
@@ -253,17 +250,22 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
     @Override
     public final <X, Z extends EffectiveStatement<X, ?>> @NonNull Optional<X> findSubstatementArgument(
             final @NonNull Class<Z> type) {
-        final E existing = effectiveInstance;
+        final E existing = effectiveInstance();
         return existing != null ? existing.findFirstEffectiveSubstatementArgument(type)
             : findSubstatementArgumentImpl(type);
     }
 
     @Override
     public final boolean hasSubstatement(final @NonNull Class<? extends EffectiveStatement<?, ?>> type) {
-        final E existing = effectiveInstance;
+        final E existing = effectiveInstance();
         return existing != null ? existing.findFirstEffectiveSubstatement(type).isPresent() : hasSubstatementImpl(type);
     }
 
+    private E effectiveInstance() {
+        final Object existing = effectiveInstance;
+        return existing != null ? EffectiveInstances.local(existing) : null;
+    }
+
     // Visible due to InferredStatementContext's override. At this point we do not have an effective instance available.
     <X, Z extends EffectiveStatement<X, ?>> @NonNull Optional<X> findSubstatementArgumentImpl(
             final @NonNull Class<Z> type) {
@@ -292,22 +294,17 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
     }
 
     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
-        return toStringHelper.add("definition", definition()).add("rawArgument", rawArgument())
-            .add("refCount", refString());
+        return toStringHelper.add("definition", definition()).add("argument", argument()).add("refCount", refString());
     }
 
     private String refString() {
         final int current = refcount;
-        switch (current) {
-            case REFCOUNT_DEFUNCT:
-                return "DEFUNCT";
-            case REFCOUNT_SWEEPING:
-                return "SWEEPING";
-            case REFCOUNT_SWEPT:
-                return "SWEPT";
-            default:
-                return String.valueOf(refcount);
-        }
+        return switch (current) {
+            case REFCOUNT_DEFUNCT -> "DEFUNCT";
+            case REFCOUNT_SWEEPING -> "SWEEPING";
+            case REFCOUNT_SWEPT -> "SWEPT";
+            default -> String.valueOf(refcount);
+        };
     }
 
     /**
@@ -324,25 +321,22 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
     //
 
     @Override
-    public final <K, V, T extends K, N extends ParserNamespace<K, V>> V namespaceItem(final Class<@NonNull N> type,
-            final T key) {
+    public final <K, V, T extends K> V namespaceItem(final ParserNamespace<K, V> type, final T key) {
         return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, key);
     }
 
     @Override
-    public final <K, V, N extends ParserNamespace<K, V>> Map<K, V> namespace(final Class<@NonNull N> type) {
+    public final <K, V> Map<K, V> namespace(final ParserNamespace<K, V> type) {
         return getNamespace(type);
     }
 
     @Override
-    public final <K, V, N extends ParserNamespace<K, V>>
-            Map<K, V> localNamespacePortion(final Class<@NonNull N> type) {
+    public final <K, V> Map<K, V> localNamespacePortion(final ParserNamespace<K, V> type) {
         return getLocalNamespace(type);
     }
 
     @Override
-    protected <K, V, N extends ParserNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
-            final V value) {
+    protected <K, V> void onNamespaceElementAdded(final ParserNamespace<K, V> type, final K key, final V value) {
         // definition().onNamespaceElementAdded(this, type, key, value);
     }
 
@@ -363,9 +357,11 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
         QNameModule targetModule);
 
     @Override
-    public final ReactorStmtCtx<A, D, E> replicaAsChildOf(final Mutable<?, ?, ?> parent) {
+    public final ReplicaStatementContext<A, D, E> replicaAsChildOf(final Mutable<?, ?, ?> parent) {
         checkArgument(parent instanceof StatementContextBase, "Unsupported parent %s", parent);
-        return replicaAsChildOf((StatementContextBase<?, ?, ?>) parent);
+        final var ret = replicaAsChildOf((StatementContextBase<?, ?, ?>) parent);
+        definition().onStatementAdded(ret);
+        return ret;
     }
 
     abstract @NonNull ReplicaStatementContext<A, D, E> replicaAsChildOf(@NonNull StatementContextBase<?, ?, ?> parent);
@@ -378,17 +374,11 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
 
     @Override
     public final E buildEffective() {
-        final E existing;
-        return (existing = effectiveInstance) != null ? existing : loadEffective();
+        final Object existing;
+        return (existing = effectiveInstance) != null ? EffectiveInstances.local(existing) : loadEffective();
     }
 
     private @NonNull E loadEffective() {
-        // Creating an effective statement does not strictly require a declared instance -- there are statements like
-        // 'input', which are implicitly defined.
-        // Our implementation design makes an invariant assumption that buildDeclared() has been called by the time
-        // we attempt to create effective statement:
-        declared();
-
         final E ret = createEffective();
         effectiveInstance = ret;
         // we have called createEffective(), substatements are no longer guarded by us. Let's see if we can clear up
@@ -401,6 +391,43 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
 
     abstract @NonNull E createEffective();
 
+    /**
+     * Routing of the request to build an effective statement from {@link InferredStatementContext} towards the original
+     * definition site. This is needed to pick the correct instantiation method: for declared statements we will
+     * eventually land in {@link AbstractResumedStatement}, for underclared statements that will be
+     * {@link UndeclaredStmtCtx}.
+     *
+     * @param factory Statement factory
+     * @param ctx Inferred statement context, i.e. where the effective statement is instantiated
+     * @return Built effective stateue
+     */
+    abstract @NonNull E createInferredEffective(@NonNull StatementFactory<A, D, E> factory,
+        @NonNull InferredStatementContext<A, D, E> ctx, Stream<? extends ReactorStmtCtx<?, ?, ?>> declared,
+        Stream<? extends ReactorStmtCtx<?, ?, ?>> effective);
+
+    /**
+     * Attach an effective copy of this statement. This essentially acts as a map, where we make a few assumptions:
+     * <ul>
+     *   <li>{@code copy} and {@code this} statement share {@link #getOriginalCtx()} if it exists</li>
+     *   <li>{@code copy} did not modify any statements relative to {@code this}</li>
+     * </ul>
+     *
+     * @param state effective statement state, acting as a lookup key
+     * @param stmt New copy to append
+     * @return {@code stmt} or a previously-created instances with the same {@code state}
+     */
+    @SuppressWarnings("unchecked")
+    final @NonNull E attachEffectiveCopy(final @NonNull EffectiveStatementState state, final @NonNull E stmt) {
+        final Object local = effectiveInstance;
+        final EffectiveInstances<E> instances;
+        if (local instanceof EffectiveInstances) {
+            instances = (EffectiveInstances<E>) local;
+        } else {
+            effectiveInstance = instances = new EffectiveInstances<>((E) local);
+        }
+        return instances.attachCopy(state, stmt);
+    }
+
     /**
      * Walk this statement's copy history and return the statement closest to original which has not had its effective
      * statements modified. This statement and returned substatement logically have the same set of substatements, hence
@@ -438,14 +465,15 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
     //
     //
 
+    // Non-final form ImplicitStmtCtx
     @Override
-    public final boolean isSupportedToBuildEffective() {
+    public boolean isSupportedToBuildEffective() {
         return isSupportedToBuildEffective;
     }
 
     @Override
-    public final void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) {
-        this.isSupportedToBuildEffective = isSupportedToBuildEffective;
+    public final void setUnsupported() {
+        this.isSupportedToBuildEffective = false;
     }
 
     @Override
@@ -464,8 +492,8 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
          */
         if (isParentSupportedByFeatures()) {
             // If the set of supported features has not been provided, all features are supported by default.
-            final Set<QName> supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class,
-                    SupportedFeatures.SUPPORTED_FEATURES);
+            final Set<QName> supportedFeatures = getFromNamespace(ParserNamespaces.SUPPORTED_FEATURES,
+                Empty.value());
             if (supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures)) {
                 flags |= SET_SUPPORTED_BY_FEATURES;
                 return true;
@@ -623,17 +651,17 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
         }
 
         final Object argument = argument();
-        if (argument instanceof QName) {
-            return ((QName) argument).getModule();
+        if (argument instanceof QName qname) {
+            return qname.getModule();
         }
-        if (argument instanceof String) {
+        if (argument instanceof String str) {
             // FIXME: This may yield illegal argument exceptions
-            return StmtContextUtils.qnameFromArgument(getOriginalCtx().orElse(this), (String) argument).getModule();
+            return StmtContextUtils.qnameFromArgument(getOriginalCtx().orElse(this), str).getModule();
         }
-        if (argument instanceof SchemaNodeIdentifier
+        if (argument instanceof SchemaNodeIdentifier sni
                 && (producesDeclared(AugmentStatement.class) || producesDeclared(RefineStatement.class)
                         || producesDeclared(DeviationStatement.class))) {
-            return ((SchemaNodeIdentifier) argument).lastNodeIdentifier().getModule();
+            return sni.lastNodeIdentifier().getModule();
         }
 
         return coerceParent().effectiveNamespace();