Fix inferred statements over undeclared statements
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / ReactorStmtCtx.java
index 5b90299c327a76c7cf4c3f71a0d6db0d96c3938f..73aef53d66b7e140caba5424019ecadf95927450 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;
@@ -33,6 +35,7 @@ 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.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 +43,12 @@ 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();
     }
 
@@ -237,12 +240,6 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
         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);
-    }
-
     //
     // In the next two methods we are looking for an effective statement. If we already have an effective instance,
     // defer to it's implementation of the equivalent search. Otherwise we search our substatement contexts.
@@ -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,8 +294,7 @@ 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() {
@@ -363,9 +364,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 +381,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 +398,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 StmtContext<?, ?, ?>> declared,
+        Stream<? extends StmtContext<?, ?, ?>> 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
@@ -444,8 +478,8 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
     }
 
     @Override
-    public final void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) {
-        this.isSupportedToBuildEffective = isSupportedToBuildEffective;
+    public final void setUnsupported() {
+        this.isSupportedToBuildEffective = false;
     }
 
     @Override
@@ -464,8 +498,7 @@ 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(SupportedFeaturesNamespace.class, Empty.value());
             if (supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures)) {
                 flags |= SET_SUPPORTED_BY_FEATURES;
                 return true;