Do not suppress 'uses' node effects
[yangtools.git] / parser / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / uses / UsesStatementSupport.java
index e8c464a0a7f86931f4bb2a3e9604aebb4ad08eef..02f8669b2233d30df797aea094240e4f43aaf377 100644 (file)
@@ -12,11 +12,9 @@ import static com.google.common.base.Verify.verifyNotNull;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashMap;
-import java.util.Map;
 import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.common.Empty;
@@ -47,10 +45,10 @@ import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.refine.RefineTargetNa
 import org.opendaylight.yangtools.yang.parser.spi.GroupingNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.SchemaTreeNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.BoundStmtCtx;
 import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
 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;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
@@ -63,7 +61,6 @@ import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
-import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -92,30 +89,31 @@ public final class UsesStatementSupport
 
     @Override
     public void onFullDefinitionDeclared(final Mutable<QName, UsesStatement, UsesEffectiveStatement> usesNode) {
-        if (!usesNode.isSupportedByFeatures()) {
-            return;
-        }
         super.onFullDefinitionDeclared(usesNode);
 
-        final ModelActionBuilder usesAction = usesNode.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL);
-        final QName groupingName = usesNode.argument();
+        final var usesAction = usesNode.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL);
+        final var groupingName = usesNode.argument();
 
-        final Prerequisite<StmtContext<?, ?, ?>> sourceGroupingPre = usesAction.requiresCtx(usesNode,
-                GroupingNamespace.class, groupingName, ModelProcessingPhase.EFFECTIVE_MODEL);
-        final Prerequisite<? extends StmtContext.Mutable<?, ?, ?>> targetNodePre = usesAction.mutatesEffectiveCtx(
-                usesNode.getParentContext());
+        final var sourceGroupingPre = usesAction.requiresCtx(usesNode, GroupingNamespace.class, groupingName,
+                ModelProcessingPhase.EFFECTIVE_MODEL);
+        final var targetNodePre = usesAction.mutatesEffectiveCtx(usesNode.getParentContext());
 
         usesAction.apply(new InferenceAction() {
 
             @Override
             public void apply(final InferenceContext ctx) {
-                final StatementContextBase<?, ?, ?> targetNodeStmtCtx =
-                        (StatementContextBase<?, ?, ?>) targetNodePre.resolve(ctx);
-                final StatementContextBase<?, ?, ?> sourceGrpStmtCtx =
-                        (StatementContextBase<?, ?, ?>) sourceGroupingPre.resolve(ctx);
+                final var targetNodeStmtCtx = targetNodePre.resolve(ctx);
+                final var sourceGrpStmtCtx = sourceGroupingPre.resolve(ctx);
 
                 copyFromSourceToTarget(sourceGrpStmtCtx, targetNodeStmtCtx, usesNode);
-                resolveUsesNode(usesNode, targetNodeStmtCtx);
+
+                // Apply any refine statements
+                for (var subStmtCtx : usesNode.mutableDeclaredSubstatements()) {
+                    if (subStmtCtx.producesDeclared(RefineStatement.class) && areFeaturesSupported(subStmtCtx)) {
+                        performRefine(subStmtCtx, targetNodeStmtCtx);
+                    }
+                }
+
                 StmtContextUtils.validateIfFeatureAndWhenOnListKeys(usesNode);
                 usesNode.addToNs(SourceGroupingNamespace.class, Empty.value(), sourceGrpStmtCtx);
             }
@@ -130,8 +128,8 @@ public final class UsesStatementSupport
     }
 
     @Override
-    protected UsesStatement createDeclared(final StmtContext<QName, UsesStatement, ?> ctx,
-            final ImmutableList<? extends DeclaredStatement<?>> substatements) {
+    protected UsesStatement createDeclared(final BoundStmtCtx<QName> ctx,
+            final ImmutableList<DeclaredStatement<?>> substatements) {
         return DeclaredStatements.createUses(ctx.getRawArgument(), ctx.getArgument(), substatements);
     }
 
@@ -169,15 +167,12 @@ public final class UsesStatementSupport
 
     static @NonNull ImmutableMap<Descendant, SchemaNode> indexRefines(
             final ImmutableList<? extends EffectiveStatement<?, ?>> substatements) {
-        final Map<Descendant, SchemaNode> refines = new LinkedHashMap<>();
-
-        for (EffectiveStatement<?, ?> effectiveStatement : substatements) {
-            if (effectiveStatement instanceof RefineEffectiveStatementImpl) {
-                final RefineEffectiveStatementImpl refineStmt = (RefineEffectiveStatementImpl) effectiveStatement;
+        final var refines = new LinkedHashMap<Descendant, SchemaNode>();
+        for (var effectiveStatement : substatements) {
+            if (effectiveStatement instanceof RefineEffectiveStatementImpl refineStmt) {
                 refines.put(refineStmt.argument(), refineStmt.getRefineTargetNode());
             }
         }
-
         return ImmutableMap.copyOf(refines);
     }
 
@@ -193,23 +188,26 @@ public final class UsesStatementSupport
      * @throws SourceException
      *             instance of SourceException
      */
-    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
-            justification = "https://github.com/spotbugs/spotbugs/issues/811")
-    private static void copyFromSourceToTarget(final Mutable<?, ?, ?> sourceGrpStmtCtx,
-            final StatementContextBase<?, ?, ?> targetCtx,
-            final Mutable<QName, UsesStatement, UsesEffectiveStatement> usesNode) {
-        final Collection<? extends Mutable<?, ?, ?>> declared = sourceGrpStmtCtx.mutableDeclaredSubstatements();
-        final Collection<? extends Mutable<?, ?, ?>> effective = sourceGrpStmtCtx.mutableEffectiveSubstatements();
-        final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
-        final QNameModule newQNameModule = getNewQNameModule(targetCtx, sourceGrpStmtCtx);
-
-        for (final Mutable<?, ?, ?> original : declared) {
-            if (original.isSupportedByFeatures() && shouldCopy(original)) {
-                original.copyAsChildOf(targetCtx, CopyType.ADDED_BY_USES, newQNameModule).ifPresent(buffer::add);
+    private static void copyFromSourceToTarget(final StmtContext<?, ?, ?> sourceGrpStmtCtx,
+            final Mutable<?, ?, ?> targetCtx, final Mutable<QName, UsesStatement, UsesEffectiveStatement> usesNode) {
+        final var declared = sourceGrpStmtCtx.declaredSubstatements();
+        final var effective = sourceGrpStmtCtx.effectiveSubstatements();
+        final var buffer = new ArrayList<Mutable<?, ?, ?>>(declared.size() + effective.size());
+        final var newQNameModule = getNewQNameModule(targetCtx, sourceGrpStmtCtx);
+        final var unsupported = !usesNode.isSupportedByFeatures();
+
+        for (var original : declared) {
+            if (shouldCopy(original)) {
+                original.copyAsChildOf(targetCtx, CopyType.ADDED_BY_USES, newQNameModule).ifPresent(copy -> {
+                    if (unsupported || !original.isSupportedByFeatures() || !original.isSupportedToBuildEffective()) {
+                        copy.setUnsupported();
+                    }
+                    buffer.add(copy);
+                });
             }
         }
 
-        for (final Mutable<?, ?, ?> original : effective) {
+        for (var original : effective) {
             if (shouldCopy(original)) {
                 original.copyAsChildOf(targetCtx, CopyType.ADDED_BY_USES, newQNameModule).ifPresent(buffer::add);
             }
@@ -263,27 +261,13 @@ public final class UsesStatementSupport
         if (targetCtx.publicDefinition() == YangStmtMapping.AUGMENT) {
             return StmtContextUtils.getRootModuleQName(targetCtx);
         }
-
-        final Object targetStmtArgument = targetCtx.argument();
-        final Object sourceStmtArgument = stmtContext.argument();
-        if (targetStmtArgument instanceof QName && sourceStmtArgument instanceof QName) {
-            return ((QName) targetStmtArgument).getModule();
+        if (targetCtx.argument() instanceof QName targetQName && stmtContext.argument() instanceof QName) {
+            return targetQName.getModule();
         }
 
         return null;
     }
 
-    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
-            justification = "https://github.com/spotbugs/spotbugs/issues/811")
-    private static void resolveUsesNode(final Mutable<QName, UsesStatement, UsesEffectiveStatement> usesNode,
-            final StmtContext<?, ?, ?> targetNodeStmtCtx) {
-        for (final Mutable<?, ?, ?> subStmtCtx : usesNode.mutableDeclaredSubstatements()) {
-            if (subStmtCtx.producesDeclared(RefineStatement.class) && areFeaturesSupported(subStmtCtx)) {
-                performRefine(subStmtCtx, targetNodeStmtCtx);
-            }
-        }
-    }
-
     private static boolean areFeaturesSupported(final StmtContext<?, ?, ?> subStmtCtx) {
         /*
          * In case of Yang 1.1, checks whether features are supported.
@@ -296,6 +280,8 @@ public final class UsesStatementSupport
         InferenceException.throwIf(!(refineArgument instanceof SchemaNodeIdentifier), subStmtCtx,
             "Invalid refine argument %s. It must be instance of SchemaNodeIdentifier.", refineArgument);
 
+        // FIXME: this really should be handled via separate inference, i.e. we first instantiate the template and when
+        //        it appears, this refine will trigger on it. This reinforces the FIXME below.
         final Optional<StmtContext<?, ?, ?>> optRefineTargetCtx = SchemaTreeNamespace.findNode(
             usesParentCtx, (SchemaNodeIdentifier) refineArgument);
         InferenceException.throwIf(!optRefineTargetCtx.isPresent(), subStmtCtx, "Refine target node %s not found.",
@@ -311,8 +297,8 @@ public final class UsesStatementSupport
                 subStmtCtx.coerceParentContext().argument(), refineTargetNodeCtx.argument(),
                 subStmtCtx.sourceReference());
         } else {
-            verify(refineTargetNodeCtx instanceof StatementContextBase);
-            addOrReplaceNodes(subStmtCtx, (StatementContextBase<?, ?, ?>) refineTargetNodeCtx);
+            verify(refineTargetNodeCtx instanceof Mutable, "Unexpected target %s", refineTargetNodeCtx);
+            addOrReplaceNodes(subStmtCtx, (Mutable<?, ?, ?>) refineTargetNodeCtx);
         }
 
         // Target is a prerequisite for the 'refine', hence if the target is not supported, the refine is not supported
@@ -324,20 +310,21 @@ public final class UsesStatementSupport
         }
     }
 
-    private static void addOrReplaceNodes(final Mutable<?, ?, ?> subStmtCtx,
-            final StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
-        for (final Mutable<?, ?, ?> refineSubstatementCtx : subStmtCtx.mutableDeclaredSubstatements()) {
+    private static void addOrReplaceNodes(final StmtContext<?, ?, ?> subStmtCtx,
+            final Mutable<?, ?, ?> refineTargetNodeCtx) {
+        for (StmtContext<?, ?, ?> refineSubstatementCtx : subStmtCtx.declaredSubstatements()) {
             if (isSupportedRefineSubstatement(refineSubstatementCtx)) {
                 addOrReplaceNode(refineSubstatementCtx, refineTargetNodeCtx);
             }
         }
     }
 
-    private static void addOrReplaceNode(final Mutable<?, ?, ?> refineSubstatementCtx,
-            final StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
+    private static void addOrReplaceNode(final StmtContext<?, ?, ?> refineSubstatementCtx,
+            final Mutable<?, ?, ?> refineTargetNodeCtx) {
 
         final StatementDefinition refineSubstatementDef = refineSubstatementCtx.publicDefinition();
 
+        // FIXME: this is quite costly, use an explicit block
         SourceException.throwIf(!isSupportedRefineTarget(refineSubstatementCtx, refineTargetNodeCtx),
                 refineSubstatementCtx,
                 "Error in module '%s' in the refine of uses '%s': can not perform refine of '%s' for the target '%s'.",
@@ -347,6 +334,7 @@ public final class UsesStatementSupport
         if (!isAllowedToAddByRefine(refineSubstatementDef)) {
             refineTargetNodeCtx.removeStatementFromEffectiveSubstatements(refineSubstatementDef);
         }
+        // FIXME: childCopyOf() should handle this through per-statement copy policy, right?
         refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx.replicaAsChildOf(refineTargetNodeCtx));
     }