Reconstruct inference stack during normalization 70/100270/15
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 25 Mar 2022 19:12:22 +0000 (20:12 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 27 Mar 2022 19:26:05 +0000 (21:26 +0200)
Centralize allocation of Inference, adjusting tests to take advantage of
this facility. This also hides the public InstanceIdentifierContext, so
that we get clarity as to the actual context in which we are executing.

AbstractFieldsTranslatorTest is refactored to remove mocking and
correctly bind to the undelying model.

This temporarily picks up yang-model-util-7.0.15 for
restconf-{common,nb-rfc8040}, so as to gain access to new methods of
DataSchemaContext{Node,Tree}.

JIRA: NETCONF-818
Change-Id: Iaf3362877261f3caaebfb4ec98ef6de792139f12
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
26 files changed:
restconf/restconf-common/pom.xml
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/context/InstanceIdentifierContext.java
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/OperationsResourceUtils.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDocumentedExceptionMapper.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/ControllerContext.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizer.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java
restconf/restconf-nb-rfc8040/pom.xml
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfImpl.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/IdentifierCodec.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParamsTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/CreateStreamUtilTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf799Test.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImplTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/AbstractFieldsTranslatorTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/NetconfFieldsTranslatorTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/WriterFieldsTranslatorTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializerTest.java

index 971be213b3d11a92930622d06f767cf85b754092..e7c9a0b323fa372141ae9d44e99847d08a8607ad 100644 (file)
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-codec-xml</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-util</artifactId>
+      <!-- FIXME: remove this override -->
+      <version>7.0.15</version>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-impl</artifactId>
index 09a5cd2a835bc8e68cb0f891a2bfe8a4abdbf059..cfe136565a566812148a602165d8757073a579b4 100644 (file)
@@ -18,139 +18,226 @@ import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
-import org.opendaylight.yangtools.yang.model.api.ContainerLike;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 
-public final class InstanceIdentifierContext {
-    private final YangInstanceIdentifier instanceIdentifier;
-    private final SchemaNode schemaNode;
-    private final DOMMountPoint mountPoint;
-    private final EffectiveModelContext schemaContext;
+public abstract class InstanceIdentifierContext {
+    private static final class Root extends InstanceIdentifierContext {
+        private final @NonNull EffectiveModelContext context;
 
-    private InstanceIdentifierContext(final EffectiveModelContext context, final DOMMountPoint mountPoint) {
-        instanceIdentifier = YangInstanceIdentifier.empty();
-        schemaContext = requireNonNull(context);
-        schemaNode = schemaContext;
-        this.mountPoint = mountPoint;
+        Root(final EffectiveModelContext context, final DOMMountPoint mountPoint) {
+            super(context, mountPoint);
+            this.context = requireNonNull(context);
+        }
+
+        @Override
+        public EffectiveModelContext getSchemaContext() {
+            return context;
+        }
+
+        @Override
+        public YangInstanceIdentifier getInstanceIdentifier() {
+            return YangInstanceIdentifier.empty();
+        }
+
+        @Override
+        public Inference inference() {
+            return SchemaInferenceStack.of(context).toInference();
+        }
+
+        @Override
+        InstanceIdentifierContext createWithConcapt(final List<PathArgument> concatArgs) {
+            return new DataPath(context, getMountPoint(), SchemaInferenceStack.of(context),
+                YangInstanceIdentifier.create(concatArgs));
+        }
     }
 
-    private InstanceIdentifierContext(final EffectiveModelContext context, final RpcDefinition rpc,
-            final DOMMountPoint mountPoint) {
-        instanceIdentifier = null;
-        schemaContext = requireNonNull(context);
-        schemaNode = requireNonNull(rpc);
-        this.mountPoint = mountPoint;
+    private static final class DataPath extends InstanceIdentifierContext {
+        private final @NonNull YangInstanceIdentifier path;
+        private final @NonNull SchemaInferenceStack stack;
+
+        private DataPath(final SchemaNode schemaNode, final DOMMountPoint mountPoint,
+                final SchemaInferenceStack stack, final YangInstanceIdentifier path) {
+            super(schemaNode, mountPoint);
+            this.stack = requireNonNull(stack);
+            this.path = requireNonNull(path);
+        }
+
+        static @NonNull DataPath of(final EffectiveModelContext context, final YangInstanceIdentifier path,
+                final DOMMountPoint mountPoint) {
+            final var nodeAndStack = DataSchemaContextTree.from(context).enterPath(path).orElseThrow();
+            return new DataPath(nodeAndStack.node().getDataSchemaNode(), mountPoint, nodeAndStack.stack(), path);
+        }
+
+        @Override
+        public YangInstanceIdentifier getInstanceIdentifier() {
+            return path;
+        }
+
+        @Override
+        public Inference inference() {
+            return stack.toInference();
+        }
+
+        @Override
+        @NonNull
+        InstanceIdentifierContext createWithConcapt(final List<PathArgument> concatArgs) {
+            final var newInstanceIdentifier = YangInstanceIdentifier.create(
+                Iterables.concat(path.getPathArguments(), concatArgs));
+            return new DataPath(getSchemaNode(), getMountPoint(), stack, newInstanceIdentifier);
+        }
     }
 
-    private InstanceIdentifierContext(final EffectiveModelContext context, final ContainerLike rpcInputOutput,
-            final DOMMountPoint mountPoint) {
-        instanceIdentifier = null;
-        schemaContext = requireNonNull(context);
-        schemaNode = requireNonNull(rpcInputOutput);
-        this.mountPoint = mountPoint;
+    private static final class WithoutDataPath extends InstanceIdentifierContext {
+        private final @NonNull SchemaInferenceStack stack;
+
+        private WithoutDataPath(final SchemaNode schemaNode, final DOMMountPoint mountPoint,
+                final SchemaInferenceStack stack) {
+            super(schemaNode, mountPoint);
+            this.stack = requireNonNull(stack);
+        }
+
+        @Override
+        public Inference inference() {
+            return stack.toInference();
+        }
+
+        @Override
+        public @Nullable YangInstanceIdentifier getInstanceIdentifier() {
+            return null;
+        }
+
+        @Override
+        InstanceIdentifierContext createWithConcapt(final List<PathArgument> concatArgs) {
+            return this;
+        }
     }
 
-    @Deprecated(forRemoval = true)
-    public InstanceIdentifierContext(final YangInstanceIdentifier instanceIdentifier, final SchemaNode schemaNode,
-            final DOMMountPoint mountPoint, final EffectiveModelContext context) {
-        this.instanceIdentifier = instanceIdentifier;
-        this.schemaNode = schemaNode;
+    private final @NonNull SchemaNode schemaNode;
+    private final @Nullable DOMMountPoint mountPoint;
+
+    InstanceIdentifierContext(final SchemaNode schemaNode, final DOMMountPoint mountPoint) {
+        this.schemaNode = requireNonNull(schemaNode);
         this.mountPoint = mountPoint;
-        schemaContext = context;
     }
 
     public static @NonNull InstanceIdentifierContext ofLocalRoot(final EffectiveModelContext context) {
-        return new InstanceIdentifierContext(context, null);
+        return new Root(context, null);
     }
 
     @VisibleForTesting
     public static @NonNull InstanceIdentifierContext ofLocalPath(final EffectiveModelContext context,
             final YangInstanceIdentifier path) {
-        return new InstanceIdentifierContext(requireNonNull(path),
-            DataSchemaContextTree.from(context).findChild(path).orElseThrow().getDataSchemaNode(), null,
-            requireNonNull(context));
+        return DataPath.of(context, path, null);
     }
 
     // Legacy bierman02 invokeRpc()
-    public static @NonNull InstanceIdentifierContext ofLocalRpc(final EffectiveModelContext context,
-        // FIXME: this this method really needed?
-            final RpcDefinition rpc) {
-        return new InstanceIdentifierContext(context, rpc, null);
+    public static @NonNull InstanceIdentifierContext ofRpcInput(final EffectiveModelContext context,
+            // FIXME: this this method really needed?
+            final RpcDefinition rpc, final @Nullable DOMMountPoint mountPoint) {
+        final var stack = SchemaInferenceStack.of(context);
+        stack.enterSchemaTree(rpc.getQName());
+        stack.enterSchemaTree(rpc.getInput().getQName());
+        return new WithoutDataPath(rpc, mountPoint, stack);
+    }
+
+    public static @NonNull InstanceIdentifierContext ofRpcOutput(final EffectiveModelContext context,
+            // FIXME: this this method really needed?
+            final RpcDefinition rpc, final @Nullable DOMMountPoint mountPoint) {
+        final var stack = SchemaInferenceStack.of(context);
+        stack.enterSchemaTree(rpc.getQName());
+        stack.enterSchemaTree(rpc.getOutput().getQName());
+        return new WithoutDataPath(rpc, mountPoint, stack);
     }
 
     // Invocations of various identifier-less details
     public static @NonNull InstanceIdentifierContext ofDataSchemaNode(final EffectiveModelContext context,
             final DataSchemaNode schemaNode) {
-        return new InstanceIdentifierContext(null, requireNonNull(schemaNode), null, requireNonNull(context));
+        return new WithoutDataPath(schemaNode, null,
+            SchemaInferenceStack.ofInstantiatedPath(context, schemaNode.getPath()));
     }
 
     // Invocations of various identifier-less details, potentially having a mount point
     public static @NonNull InstanceIdentifierContext ofDataSchemaNode(final EffectiveModelContext context,
             final DataSchemaNode schemaNode, final @Nullable DOMMountPoint mountPoint) {
-        return new InstanceIdentifierContext(null, requireNonNull(schemaNode), mountPoint, requireNonNull(context));
+        return new WithoutDataPath(schemaNode, mountPoint,
+            SchemaInferenceStack.ofSchemaPath(context, schemaNode.getPath()));
     }
 
     public static @NonNull InstanceIdentifierContext ofLocalRpcInput(final EffectiveModelContext context,
             // FIXME: this this method really needed?
             final RpcDefinition rpc) {
-        return new InstanceIdentifierContext(context, rpc.getInput(), null);
+        final var stack = SchemaInferenceStack.of(context);
+        stack.enterSchemaTree(rpc.getQName());
+        stack.enterSchemaTree(rpc.getInput().getQName());
+        return new WithoutDataPath(rpc.getInput(), null, stack);
     }
 
     public static @NonNull InstanceIdentifierContext ofLocalRpcOutput(final EffectiveModelContext context,
             // FIXME: we want to re-validate this, so might as well take a QName
             final RpcDefinition rpc) {
-        return new InstanceIdentifierContext(context, rpc, null);
+        final var stack = SchemaInferenceStack.of(context);
+        stack.enterSchemaTree(rpc.getQName());
+        stack.enterSchemaTree(rpc.getOutput().getQName());
+        return new WithoutDataPath(rpc, null, stack);
+    }
+
+    public static @NonNull InstanceIdentifierContext ofAction(final SchemaInferenceStack stack,
+            final ActionDefinition schemaNode, final YangInstanceIdentifier path,
+            final @Nullable DOMMountPoint mountPoint) {
+        return new DataPath(schemaNode, mountPoint, stack, path);
+    }
+
+    public static @NonNull InstanceIdentifierContext ofPath(final SchemaInferenceStack stack,
+            final SchemaNode schemaNode, final YangInstanceIdentifier path,
+            final @Nullable DOMMountPoint mountPoint) {
+        return new DataPath(schemaNode, mountPoint, stack, path);
     }
 
     public static @NonNull InstanceIdentifierContext ofMountPointRoot(final DOMMountPoint mountPoint,
             final EffectiveModelContext mountContext) {
-        return new InstanceIdentifierContext(mountContext, requireNonNull(mountPoint));
+        return new Root(mountContext, requireNonNull(mountPoint));
     }
 
     @VisibleForTesting
     public static @NonNull InstanceIdentifierContext ofMountPointPath(final DOMMountPoint mountPoint,
             final EffectiveModelContext context, final YangInstanceIdentifier path) {
-        return new InstanceIdentifierContext(requireNonNull(path),
-            DataSchemaContextTree.from(context).findChild(path).orElseThrow().getDataSchemaNode(),
-            requireNonNull(mountPoint), requireNonNull(context));
-    }
-
-    public static @NonNull InstanceIdentifierContext ofMountPointRpc(final DOMMountPoint mountPoint,
-            final EffectiveModelContext mountContext, final RpcDefinition rpc) {
-        return new InstanceIdentifierContext(mountContext, rpc, requireNonNull(mountPoint));
+        return DataPath.of(context, path, requireNonNull(mountPoint));
     }
 
     public static @NonNull InstanceIdentifierContext ofMountPointRpcOutput(final DOMMountPoint mountPoint,
             final EffectiveModelContext mountContext, final RpcDefinition rpc) {
-        return new InstanceIdentifierContext(mountContext, rpc, requireNonNull(mountPoint));
+        final var stack = SchemaInferenceStack.of(mountContext);
+        stack.enterSchemaTree(rpc.getQName());
+        stack.enterSchemaTree(rpc.getOutput().getQName());
+        return new WithoutDataPath(rpc, requireNonNull(mountPoint), stack);
     }
 
     // FIXME: what the heck are the callers of this doing?!
-    public @NonNull InstanceIdentifierContext withConcatenatedArgs(final List<PathArgument> concatArgs) {
-        if (instanceIdentifier == null || concatArgs.isEmpty()) {
-            return this;
-        }
-        final var newInstanceIdentifier = YangInstanceIdentifier.create(
-            Iterables.concat(instanceIdentifier.getPathArguments(), concatArgs));
-        return new InstanceIdentifierContext(newInstanceIdentifier, schemaNode, mountPoint, schemaContext);
+    public final @NonNull InstanceIdentifierContext withConcatenatedArgs(final List<PathArgument> concatArgs) {
+        return concatArgs.isEmpty() ? this : createWithConcapt(concatArgs);
     }
 
-    public YangInstanceIdentifier getInstanceIdentifier() {
-        return instanceIdentifier;
-    }
+    abstract @NonNull InstanceIdentifierContext createWithConcapt(List<PathArgument> concatArgs);
 
-    public SchemaNode getSchemaNode() {
+    public final @NonNull SchemaNode getSchemaNode() {
         return schemaNode;
     }
 
-    public DOMMountPoint getMountPoint() {
+    public final @Nullable DOMMountPoint getMountPoint() {
         return mountPoint;
     }
 
-    public EffectiveModelContext getSchemaContext() {
-        return schemaContext;
+    public @NonNull EffectiveModelContext getSchemaContext() {
+        return inference().getEffectiveModelContext();
     }
+
+    public abstract @NonNull Inference inference();
+
+    public abstract @Nullable YangInstanceIdentifier getInstanceIdentifier();
 }
index 7460f92aaa7742d02bc9401075975456181cea0a..055e57ad0f1f05fb854903fe312f9ac97cdef9a3 100644 (file)
@@ -60,7 +60,8 @@ public final class OperationsResourceUtils {
             operationsBuilder.withChild(ImmutableNodes.leafNode(leaf.getQName(), Empty.value()));
         }
 
-        return Map.entry(new InstanceIdentifierContext(null, operatationsSchema, mountPoint,
-            new OperationsEffectiveModuleContext(ImmutableSet.copyOf(modules))), operationsBuilder.build());
+        return Map.entry(InstanceIdentifierContext.ofDataSchemaNode(
+            new OperationsEffectiveModuleContext(ImmutableSet.copyOf(modules)), operatationsSchema, mountPoint),
+            operationsBuilder.build());
     }
 }
index a3865540122ea4c4e42b085f8eb910f7f0fffb0b..aecea7a17f63a1758cd9129dad4c0c03f7427618 100644 (file)
@@ -46,9 +46,8 @@ import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
 import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -109,21 +108,23 @@ public class JsonNormalizedNodeBodyReader
         final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
         final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
 
-        final SchemaInferenceStack parentSchema;
-        if (isPost) {
-            // FIXME: We need dispatch for RPC.
-            parentSchema = SchemaInferenceStack.ofSchemaPath(path.getSchemaContext(), path.getSchemaNode().getPath());
-        } else if (path.getSchemaNode() instanceof SchemaContext
-                || SchemaPath.ROOT.equals(path.getSchemaNode().getPath().getParent())) {
-            parentSchema = SchemaInferenceStack.of(path.getSchemaContext());
+        final Inference parentInference;
+        if (isPost && !(path.getSchemaNode() instanceof RpcDefinition)) {
+            parentInference = path.inference();
         } else {
-            parentSchema = SchemaInferenceStack.ofSchemaPath(path.getSchemaContext(),
-                path.getSchemaNode().getPath().getParent());
+            final var inference = path.inference();
+            if (!inference.statementPath().isEmpty()) {
+                final var stack = inference.toSchemaInferenceStack();
+                stack.exit();
+                parentInference = stack.toInference();
+            } else {
+                parentInference = inference;
+            }
         }
 
         final JsonParserStream jsonParser = JsonParserStream.create(writer,
             JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(path.getSchemaContext()),
-            parentSchema.toInference());
+            parentInference);
         final JsonReader reader = new JsonReader(new InputStreamReader(nonEmptyInputStreamOptional.get(),
                 StandardCharsets.UTF_8));
         jsonParser.parse(reader);
index 1803a4b3ba20bedb649d3e1c6e5f6ac016db1309..3975aa7de5fbb532a082bfcbb5d25188637bb8bd 100644 (file)
@@ -144,7 +144,7 @@ public class RestconfDocumentedExceptionMapper implements ExceptionMapper<Restco
         errContBuild.withChild(listErorsBuilder.build());
 
         final NormalizedNodeContext errContext = new NormalizedNodeContext(InstanceIdentifierContext.ofDataSchemaNode(
-            controllerContext.getGlobalSchema(), (DataSchemaNode) errorsSchemaNode), errContBuild.build());
+            controllerContext.getGlobalSchema(), (DataSchemaNode) errorsSchemaNode, null), errContBuild.build());
 
         Object responseBody;
         if (mediaType.getSubtype().endsWith("json")) {
index 1a34ef7bd507521f95dd90503d3d3d1b632f4c1b..56ebcf546fde9917278a55803f50d9ac09267b31 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.netconf.sal.restconf.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.Splitter;
@@ -561,7 +562,7 @@ public final class ControllerContext implements EffectiveModelContextListener, C
                             ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
                 }
 
-                final YangInstanceIdentifier partialPath = dataNormalizer.toNormalized(builder.build());
+                final YangInstanceIdentifier partialPath = dataNormalizer.toNormalized(builder.build()).getKey();
                 final Optional<DOMMountPoint> mountOpt = mountService.getMountPoint(partialPath);
                 if (mountOpt.isEmpty()) {
                     LOG.debug("Instance identifier to missing mount point: {}", partialPath);
@@ -631,8 +632,8 @@ public final class ControllerContext implements EffectiveModelContextListener, C
                     rpc = getRpcDefinition(module, rpcName);
                 }
                 if (rpc != null) {
-                    return mountPoint == null ? InstanceIdentifierContext.ofLocalRpc(globalSchema, rpc)
-                        : InstanceIdentifierContext.ofMountPointRpc(mountPoint, getModelContext(mountPoint), rpc);
+                    final var ctx = mountPoint == null ? globalSchema : getModelContext(mountPoint);
+                    return InstanceIdentifierContext.ofRpcInput(ctx, rpc, mountPoint);
                 }
             }
 
@@ -717,8 +718,19 @@ public final class ControllerContext implements EffectiveModelContextListener, C
     private static InstanceIdentifierContext createContext(final YangInstanceIdentifier instance,
             final DataSchemaNode dataSchemaNode, final DOMMountPoint mountPoint,
             final EffectiveModelContext schemaContext) {
-        final YangInstanceIdentifier instanceIdentifier = new DataNormalizer(schemaContext).toNormalized(instance);
-        return new InstanceIdentifierContext(instanceIdentifier, dataSchemaNode, mountPoint, schemaContext);
+        final var normalized = new DataNormalizer(schemaContext).toNormalized(instance);
+
+        // FIXME: Verification before we trust this
+        final var stack = normalized.getValue();
+        if (!stack.isEmpty()) {
+            final var stackPath = stack.toSchemaPath();
+            final var nodePath = dataSchemaNode.getPath();
+            verify(stackPath.equals(nodePath), "Mismatched path: expected %s got %s", nodePath, stackPath);
+        } else {
+            verify(dataSchemaNode.equals(schemaContext), "Unexpected node %s", dataSchemaNode);
+        }
+
+        return InstanceIdentifierContext.ofPath(normalized.getValue(), dataSchemaNode, normalized.getKey(), mountPoint);
     }
 
     public static DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container,
@@ -953,14 +965,6 @@ public final class ControllerContext implements EffectiveModelContextListener, C
         return builder.toString();
     }
 
-    public YangInstanceIdentifier toNormalized(final YangInstanceIdentifier legacy) {
-        try {
-            return dataNormalizer.toNormalized(legacy);
-        } catch (final NullPointerException e) {
-            throw new RestconfDocumentedException("Data normalizer isn't set. Normalization isn't possible", e);
-        }
-    }
-
     public YangInstanceIdentifier toXpathRepresentation(final YangInstanceIdentifier instanceIdentifier) {
         try {
             return dataNormalizer.toLegacy(instanceIdentifier);
index 81224b5f36827af0f72d987b5644520c887b8976..334a6c741388d91073dfac920664886d929ad3cd 100644 (file)
@@ -8,38 +8,45 @@
 package org.opendaylight.netconf.sal.restconf.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
 
 class DataNormalizer {
     private final DataNormalizationOperation<?> operation;
+    private final EffectiveModelContext context;
 
     DataNormalizer(final EffectiveModelContext ctx) {
+        context = requireNonNull(ctx);
         operation = DataNormalizationOperation.from(ctx);
     }
 
-    YangInstanceIdentifier toNormalized(final YangInstanceIdentifier legacy) {
+    Entry<YangInstanceIdentifier, SchemaInferenceStack> toNormalized(final YangInstanceIdentifier legacy) {
         List<PathArgument> normalizedArgs = new ArrayList<>();
 
         DataNormalizationOperation<?> currentOp = operation;
         Iterator<PathArgument> arguments = legacy.getPathArguments().iterator();
+        SchemaInferenceStack stack = SchemaInferenceStack.of(context);
 
         try {
             while (arguments.hasNext()) {
                 PathArgument legacyArg = arguments.next();
-                currentOp = currentOp.getChild(legacyArg);
+                currentOp = currentOp.enterChild(legacyArg, stack);
                 checkArgument(currentOp != null,
                         "Legacy Instance Identifier %s is not correct. Normalized Instance Identifier so far %s",
                         legacy, normalizedArgs);
                 while (currentOp.isMixin()) {
                     normalizedArgs.add(currentOp.getIdentifier());
-                    currentOp = currentOp.getChild(legacyArg.getNodeType());
+                    currentOp = currentOp.enterChild(legacyArg.getNodeType(), stack);
                 }
                 normalizedArgs.add(legacyArg);
             }
@@ -47,7 +54,7 @@ class DataNormalizer {
             throw new IllegalArgumentException("Failed to normalize path " + legacy, e);
         }
 
-        return YangInstanceIdentifier.create(normalizedArgs);
+        return Map.entry(YangInstanceIdentifier.create(normalizedArgs), stack);
     }
 
     DataNormalizationOperation<?> getOperation(final YangInstanceIdentifier legacy)
index 409033933c96bc6239cfcedef6e08438be0d6212..bdcf24dbb09cbd701db6273cfee1e6c6da3eb984 100644 (file)
@@ -219,8 +219,9 @@ public final class RestconfImpl implements RestconfService {
                 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
         moduleContainerBuilder.withChild(allModuleMap);
 
-        return new NormalizedNodeContext(InstanceIdentifierContext.ofDataSchemaNode(schemaContext, modulesSchemaNode),
-                moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo));
+        return new NormalizedNodeContext(
+            InstanceIdentifierContext.ofDataSchemaNode(schemaContext, modulesSchemaNode, null),
+            moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo));
     }
 
     /**
@@ -251,8 +252,8 @@ public final class RestconfImpl implements RestconfService {
         moduleContainerBuilder.withChild(mountPointModulesMap);
 
         return new NormalizedNodeContext(
-                InstanceIdentifierContext.ofDataSchemaNode(controllerContext.getGlobalSchema(), modulesSchemaNode),
-                moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo));
+            InstanceIdentifierContext.ofDataSchemaNode(controllerContext.getGlobalSchema(), modulesSchemaNode, null),
+            moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo));
     }
 
     @Override
@@ -448,10 +449,9 @@ public final class RestconfImpl implements RestconfService {
         }
 
         final var resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
-        final var info = mountPoint == null ? InstanceIdentifierContext.ofLocalRpc(schemaContext, resultNodeSchema)
-            : InstanceIdentifierContext.ofMountPointRpcOutput(mountPoint, schemaContext, resultNodeSchema);
-
-        return new NormalizedNodeContext(info, resultData, QueryParametersParser.parseWriterParameters(uriInfo));
+        return new NormalizedNodeContext(
+            InstanceIdentifierContext.ofRpcOutput(schemaContext, resultNodeSchema, mountPoint), resultData,
+            QueryParametersParser.parseWriterParameters(uriInfo));
     }
 
     @SuppressFBWarnings(value = "NP_LOAD_OF_KNOWN_NULL_VALUE",
index 395f6445103e08a307abdbf3f7ac5077707b192a..04cbb967ff6b34be56dadc7c0b58621f55c4118a 100644 (file)
@@ -82,6 +82,7 @@ import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
 
 /**
  * Unit tests for BrokerFacade.
@@ -364,8 +365,8 @@ public class BrokerFacadeTest {
 
         when(patchContext.getData()).thenReturn(List.of());
         // no mount point
-        doReturn(InstanceIdentifierContext.ofDataSchemaNode(
-            mock(EffectiveModelContext.class), mock(DataSchemaNode.class)))
+        doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)),
+            mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), null))
                 .when(patchContext).getInstanceIdentifierContext();
 
         doReturn(CommitInfo.emptyFluentFuture()).when(rwTransaction).commit();
@@ -388,8 +389,8 @@ public class BrokerFacadeTest {
 
         when(patchContext.getData()).thenReturn(List.of());
         // return mount point with broker
-        doReturn(InstanceIdentifierContext.ofDataSchemaNode(
-            mock(EffectiveModelContext.class), mock(DataSchemaNode.class), mountPoint))
+        doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)),
+            mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), mountPoint))
                 .when(patchContext).getInstanceIdentifierContext();
 
         when(mountPoint.getService(DOMDataBroker.class)).thenReturn(Optional.of(mountDataBroker));
@@ -412,8 +413,8 @@ public class BrokerFacadeTest {
         final PatchContext patchContext = mock(PatchContext.class);
         final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
 
-        doReturn(InstanceIdentifierContext.ofDataSchemaNode(
-            mock(EffectiveModelContext.class), mock(DataSchemaNode.class), mountPoint))
+        doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)),
+            mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), mountPoint))
                 .when(patchContext).getInstanceIdentifierContext();
 
         // missing broker on mounted device
index 4aa1c8ade8da5ec93c32794e523e7c4af3ffce41..56472e534b818517efdacd872c758b94186a9734 100644 (file)
@@ -148,7 +148,7 @@ public class InvokeRpcMethodTest {
         contNode.withChild(lfNode);
         container.withChild(contNode.build());
 
-        return new NormalizedNodeContext(InstanceIdentifierContext.ofLocalRpc(schema, rpcSchemaNode),
+        return new NormalizedNodeContext(InstanceIdentifierContext.ofRpcInput(schema, rpcSchemaNode, null),
             container.build());
     }
 
index 8460a1afc8b065c988caf5e11868d5b51a99ef37..dfc48e73e6064a95834fcedb346f0d0aefbdd05c 100644 (file)
@@ -17,6 +17,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 
 import com.google.common.collect.Iterables;
@@ -58,6 +59,7 @@ import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
@@ -69,11 +71,17 @@ import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNode
 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 
@@ -146,18 +154,39 @@ public class RestconfImplTest {
 
     @Test
     public void testRpcForMountpoint() throws Exception {
+        final QName qname = QName.create("namespace", "2010-10-10", "localname");
         final UriInfo uriInfo = mock(UriInfo.class);
         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters(anyBoolean());
 
         final NormalizedNodeContext ctx = mock(NormalizedNodeContext.class);
-        final RpcDefinition schemaNode = mock(RpcDefinition.class);
-        doReturn(mock(SchemaPath.class)).when(schemaNode).getPath();
-        doReturn(QName.create("namespace", "2010-10-10", "localname")).when(schemaNode).getQName();
+        final RpcDefinition rpc = mock(RpcDefinition.class,
+            withSettings().extraInterfaces(RpcEffectiveStatement.class));
+        doReturn(mock(SchemaPath.class)).when(rpc).getPath();
+        doReturn(qname).when(rpc).getQName();
+
+        final InputSchemaNode input = mock(InputSchemaNode.class,
+            withSettings().extraInterfaces(InputEffectiveStatement.class));
+        final QName inputQName = YangConstants.operationInputQName(qname.getModule());
+        doReturn(input).when(rpc).getInput();
+        doReturn(inputQName).when(input).getQName();
+        doReturn(Optional.of(input)).when((RpcEffectiveStatement) rpc).findSchemaTreeNode(inputQName);
+
+        final OutputSchemaNode output = mock(OutputSchemaNode.class,
+            withSettings().extraInterfaces(OutputEffectiveStatement.class));
+        final QName outputQName = YangConstants.operationInputQName(qname.getModule());
+        doReturn(output).when(rpc).getOutput();
+        doReturn(outputQName).when(output).getQName();
+        doReturn(Optional.of(output)).when((RpcEffectiveStatement) rpc).findSchemaTreeNode(outputQName);
+
+        final EffectiveModelContext mountContext = mock(EffectiveModelContext.class);
+        final ModuleEffectiveStatement mountModule = mock(ModuleEffectiveStatement.class);
+        doReturn(Map.of(qname.getModule(), mountModule)).when(mountContext).getModuleStatements();
+        doReturn(Optional.of(rpc)).when(mountModule).findSchemaTreeNode(qname);
 
         final DOMMountPoint mount = mock(DOMMountPoint.class);
-        doReturn(Optional.of(FixedDOMSchemaService.of(schemaContext))).when(mount).getService(DOMSchemaService.class);
+        doReturn(Optional.of(FixedDOMSchemaService.of(mountContext))).when(mount).getService(DOMSchemaService.class);
 
-        doReturn(InstanceIdentifierContext.ofMountPointRpc(mount, schemaContext, schemaNode))
+        doReturn(InstanceIdentifierContext.ofRpcInput(mountContext, rpc, mount))
             .when(ctx).getInstanceIdentifierContext();
 
         final DOMRpcService rpcService = mock(DOMRpcService.class);
@@ -174,15 +203,17 @@ public class RestconfImplTest {
      */
     @Test
     public void createNotificationStreamTest() {
-        final NormalizedNodeContext payload = mock(NormalizedNodeContext.class);
+        final QName rpcQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote",
+            "2014-01-14", "create-notification-stream");
 
-        final RpcDefinition schemaNode = mock(RpcDefinition.class);
-        doReturn(mock(SchemaPath.class)).when(schemaNode).getPath();
-        doReturn(InstanceIdentifierContext.ofLocalRpc(schemaContext, schemaNode)).when(payload)
-                .getInstanceIdentifierContext();
+        final RpcDefinition schemaNode = schemaContext.getOperations().stream()
+            .filter(rpc -> rpc.getQName().equals(rpcQName))
+            .findFirst()
+            .orElseThrow();
 
-        doReturn(QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote",
-                "2014-01-14", "create-notification-stream")).when(schemaNode).getQName();
+        final NormalizedNodeContext payload = mock(NormalizedNodeContext.class);
+        doReturn(InstanceIdentifierContext.ofRpcInput(schemaContext, schemaNode, null)).when(payload)
+                .getInstanceIdentifierContext();
 
         final Set<DataContainerChild> children = new HashSet<>();
         final LeafSetNode child = mock(LeafSetNode.class);
index a022c0e789e24848ed2a1992a4b6175a882737e9..1d36857b83cccf6c932ba28b626443913b345dda 100644 (file)
@@ -169,6 +169,6 @@ public class URIParametersParsing {
         when(rpcDef.getPath()).thenReturn(SchemaPath.create(true, rpcQName));
         when(rpcDef.getQName()).thenReturn(rpcQName);
 
-        return new NormalizedNodeContext(InstanceIdentifierContext.ofLocalRpc(schema, rpcDef), container.build());
+        return new NormalizedNodeContext(InstanceIdentifierContext.ofRpcInput(schema, rpcDef, null), container.build());
     }
 }
index 89154c9a1cc6f01c169ee7cd88b58000db53eea0..0818e8149df0f7635217d09d188c3e2a79dcfaf6 100644 (file)
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-util</artifactId>
+      <!-- FIXME: remove this override -->
+      <version>7.0.15</version>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-impl</artifactId>
index 60a21d4ee0d90038f651aeba84d1fa234c70f21f..69a1fdb8d571561f6381f1cde2965621b8d3124b 100644 (file)
@@ -330,24 +330,16 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final Absolute schemaPath = Absolute.of(ImmutableList.copyOf(context.getSchemaNode().getPath()
             .getPathFromRoot()));
         final DOMActionResult response;
-        final EffectiveModelContext schemaContextRef;
         if (mountPoint != null) {
             response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, mountPoint);
-            schemaContextRef = modelContext(mountPoint);
         } else {
             response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, actionService);
-            schemaContextRef = schemaContextHandler.get();
         }
         final DOMActionResult result = checkActionResponse(response);
 
-        ActionDefinition resultNodeSchema = null;
         ContainerNode resultData = null;
         if (result != null) {
-            final Optional<ContainerNode> optOutput = result.getOutput();
-            if (optOutput.isPresent()) {
-                resultData = optOutput.get();
-                resultNodeSchema = (ActionDefinition) context.getSchemaNode();
-            }
+            resultData = result.getOutput().orElse(null);
         }
 
         if (resultData != null && resultData.isEmpty()) {
@@ -355,9 +347,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         }
 
         return Response.status(Status.OK)
-            .entity(NormalizedNodePayload.ofNullable(
-                new InstanceIdentifierContext(yangIIdContext, resultNodeSchema, mountPoint, schemaContextRef),
-                resultData))
+            .entity(NormalizedNodePayload.ofNullable(context, resultData))
             .build();
     }
 
index 31065714263805b70a3634bc39d0d5cdcd0b0f68..915654dbaf6f39862b1a6ced050c1fd4319cf104 100644 (file)
@@ -50,7 +50,7 @@ public class RestconfImpl implements RestconfService {
             (LeafSchemaNode) ((ContainerSchemaNode) grouping.getDataChildByName(Restconf.QNAME))
             .getDataChildByName(YANG_LIBRARY_VERSION);
 
-        return NormalizedNodePayload.of(InstanceIdentifierContext.ofDataSchemaNode(context, schemaNode),
+        return NormalizedNodePayload.of(InstanceIdentifierContext.ofDataSchemaNode(context, schemaNode, null),
             ImmutableNodes.leafNode(YANG_LIBRARY_VERSION, IetfYangLibrary.REVISION.toString()));
     }
 }
index cda1b4459c12322b5748b27c504b24f9fd84b30c..9e89ddc267b6da831d4adc2d9785d12a5574c3b1 100644 (file)
@@ -27,6 +27,6 @@ public final class IdentifierCodec {
 
     public static YangInstanceIdentifier deserialize(final String data, final EffectiveModelContext schemaContext) {
         return data == null ? YangInstanceIdentifier.empty()
-            : YangInstanceIdentifier.create(YangInstanceIdentifierDeserializer.create(schemaContext, data));
+            : YangInstanceIdentifierDeserializer.create(schemaContext, data).path;
     }
 }
index d4aabad83e9c7261890e32b7e61d60fb3d3263cc..814840657920b5b9d7aed3388c717ec70df9b135 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.restconf.nb.rfc8040.utils.parser;
 
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Verify.verifyNotNull;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Splitter;
@@ -37,16 +36,9 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.common.YangNames;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
-import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
-import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -117,55 +109,14 @@ public final class ParserIdentifier {
      */
     private static InstanceIdentifierContext createIIdContext(final EffectiveModelContext schemaContext,
             final String url, final @Nullable DOMMountPoint mountPoint) {
-        final YangInstanceIdentifier urlPath = IdentifierCodec.deserialize(url, schemaContext);
         // First things first: an empty path means data invocation on SchemaContext
-        if (urlPath.isEmpty()) {
+        if (url == null) {
             return mountPoint != null ? InstanceIdentifierContext.ofMountPointRoot(mountPoint, schemaContext)
                 : InstanceIdentifierContext.ofLocalRoot(schemaContext);
         }
 
-        return new InstanceIdentifierContext(urlPath, getPathSchema(schemaContext, urlPath), mountPoint, schemaContext);
-    }
-
-    private static SchemaNode getPathSchema(final EffectiveModelContext schemaContext,
-            final YangInstanceIdentifier urlPath) {
-        // Peel the last component and locate the parent data node, empty path resolves to SchemaContext
-        final DataSchemaContextNode<?> parent = DataSchemaContextTree.from(schemaContext)
-                .findChild(verifyNotNull(urlPath.getParent()))
-                .orElseThrow(
-                    // Parent data node is not present, this is not a valid location.
-                    () -> new RestconfDocumentedException("Parent of " + urlPath + " not found",
-                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE));
-
-        // Now try to resolve the last component as a data item...
-        final DataSchemaContextNode<?> data = parent.getChild(urlPath.getLastPathArgument());
-        if (data != null) {
-            return data.getDataSchemaNode();
-        }
-
-        // ... otherwise this has to be an operation invocation. RPCs cannot be defined anywhere but schema root,
-        // actions can reside everywhere else (and SchemaContext reports them empty)
-        final QName qname = urlPath.getLastPathArgument().getNodeType();
-        final DataSchemaNode parentSchema = parent.getDataSchemaNode();
-        if (parentSchema instanceof SchemaContext) {
-            for (final RpcDefinition rpc : ((SchemaContext) parentSchema).getOperations()) {
-                if (qname.equals(rpc.getQName())) {
-                    return rpc;
-                }
-            }
-        }
-        if (parentSchema instanceof ActionNodeContainer) {
-            for (final ActionDefinition action : ((ActionNodeContainer) parentSchema).getActions()) {
-                if (qname.equals(action.getQName())) {
-                    return action;
-                }
-            }
-        }
-
-        // No luck: even if we found the parent, we did not locate a data, nor RPC, nor action node, hence the URL
-        //          is deemed invalid
-        throw new RestconfDocumentedException("Context for " + urlPath + " not found", ErrorType.PROTOCOL,
-            ErrorTag.INVALID_VALUE);
+        final var result = YangInstanceIdentifierDeserializer.create(schemaContext, url);
+        return InstanceIdentifierContext.ofPath(result.stack, result.node, result.path, mountPoint);
     }
 
     /**
index d4c6b1cd4ecbce3d06614f932baf4abeec1ff898..dd206f84430f80bc072ca6c5b4cca2763e1ee565 100644 (file)
@@ -7,10 +7,10 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.utils.parser;
 
+import static com.google.common.base.Verify.verify;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.text.ParseException;
 import java.util.ArrayList;
@@ -37,7 +37,9 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
@@ -49,6 +51,34 @@ import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
  * Deserializer for {@link String} to {@link YangInstanceIdentifier} for restconf.
  */
 public final class YangInstanceIdentifierDeserializer {
+    public static final class Result {
+        public final @NonNull YangInstanceIdentifier path;
+        public final @NonNull SchemaInferenceStack stack;
+        public final @NonNull SchemaNode node;
+
+        Result(final EffectiveModelContext context) {
+            path = YangInstanceIdentifier.empty();
+            node = requireNonNull(context);
+            stack = SchemaInferenceStack.of(context);
+        }
+
+        Result(final EffectiveModelContext context, final QName qname) {
+            // Legacy behavior: RPCs do not really have a YangInstanceIdentifier, but the rest of the code expects it
+            path = YangInstanceIdentifier.of(qname);
+            stack = SchemaInferenceStack.of(context);
+
+            final var stmt = stack.enterSchemaTree(qname);
+            verify(stmt instanceof RpcDefinition, "Unexpected statement %s", stmt);
+            node = (RpcDefinition) stmt;
+        }
+
+        Result(final List<PathArgument> steps, final SchemaInferenceStack stack, final SchemaNode node) {
+            path = YangInstanceIdentifier.create(steps);
+            this.stack = requireNonNull(stack);
+            this.node = requireNonNull(node);
+        }
+    }
+
     private final @NonNull EffectiveModelContext schemaContext;
     private final @NonNull ApiPath apiPath;
 
@@ -65,7 +95,7 @@ public final class YangInstanceIdentifierDeserializer {
      * @return {@link Iterable} of {@link PathArgument}
      * @throws RestconfDocumentedException the path is not valid
      */
-    public static List<PathArgument> create(final EffectiveModelContext schemaContext, final String data) {
+    public static Result create(final EffectiveModelContext schemaContext, final String data) {
         final ApiPath path;
         try {
             path = ApiPath.parse(requireNonNull(data));
@@ -76,7 +106,7 @@ public final class YangInstanceIdentifierDeserializer {
         return create(schemaContext, path);
     }
 
-    public static List<PathArgument> create(final EffectiveModelContext schemaContext, final ApiPath path) {
+    public static Result create(final EffectiveModelContext schemaContext, final ApiPath path) {
         return new YangInstanceIdentifierDeserializer(schemaContext, path).parse();
     }
 
@@ -88,10 +118,10 @@ public final class YangInstanceIdentifierDeserializer {
     //
     // All of this really is an utter mess because we end up calling into this code from various places which,
     // for example, should not allow RPCs to be valid targets
-    private ImmutableList<PathArgument> parse() {
+    private Result parse() {
         final var it = apiPath.steps().iterator();
         if (!it.hasNext()) {
-            return ImmutableList.of();
+            return new Result(schemaContext);
         }
 
         // First step is somewhat special:
@@ -120,16 +150,19 @@ public final class YangInstanceIdentifierDeserializer {
                     + "therefore it must not contain key values", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
             }
 
-            // Legacy behavior: RPCs do not really have a YangInstanceIdentifier, but the rest of the code expects it
-            return ImmutableList.of(new NodeIdentifier(qname));
+            return new Result(schemaContext, optRpc.orElseThrow().argument());
         }
 
+        final var stack = SchemaInferenceStack.of(schemaContext);
         final var path = new ArrayList<PathArgument>();
+        final SchemaNode node;
+
         var parentNode = DataSchemaContextTree.from(schemaContext).getRoot();
         while (true) {
             final var parentSchema = parentNode.getDataSchemaNode();
             if (parentSchema instanceof ActionNodeContainer) {
-                if (((ActionNodeContainer) parentSchema).findAction(qname).isPresent()) {
+                final var optAction = ((ActionNodeContainer) parentSchema).findAction(qname);
+                if (optAction.isPresent()) {
                     if (it.hasNext()) {
                         throw new RestconfDocumentedException("Request path resolves to action '" + qname + "' and "
                             + "therefore it must not continue past it", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
@@ -142,19 +175,21 @@ public final class YangInstanceIdentifierDeserializer {
 
                     // Legacy behavior: Action's path should not include its path, but the rest of the code expects it
                     path.add(new NodeIdentifier(qname));
+                    stack.enterSchemaTree(qname);
+                    node = optAction.orElseThrow();
                     break;
                 }
             }
 
             // Resolve the child step with respect to data schema tree
-            final var found = RestconfDocumentedException.throwIfNull(parentNode.getChild(qname),
+            final var found = RestconfDocumentedException.throwIfNull(parentNode.enterChild(stack, qname),
                 ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "Schema for '%s' not found", qname);
 
             // Now add all mixins encountered to the path
             var childNode = found;
             while (childNode.isMixin()) {
                 path.add(childNode.getIdentifier());
-                childNode = verifyNotNull(childNode.getChild(qname),
+                childNode = verifyNotNull(childNode.enterChild(stack, qname),
                     "Mixin %s is missing child for %s while resolving %s", childNode, qname, found);
             }
 
@@ -175,6 +210,7 @@ public final class YangInstanceIdentifierDeserializer {
             path.add(pathArg);
 
             if (!it.hasNext()) {
+                node = childNode.getDataSchemaNode();
                 break;
             }
 
@@ -188,7 +224,7 @@ public final class YangInstanceIdentifierDeserializer {
             qname = step.identifier().bindTo(namespace);
         }
 
-        return ImmutableList.copyOf(path);
+        return new Result(path, stack, node);
     }
 
     private NodeIdentifierWithPredicates prepareNodeWithPredicates(final QName qname,
index 231620c7899cecdfc79e48537902da4b14dd9da4..888a5a56d974270d8b6d75c9d9eee19f9a3c7823 100644 (file)
@@ -15,8 +15,11 @@ import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.withSettings;
 
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
 import javax.ws.rs.core.MultivaluedHashMap;
@@ -40,6 +43,9 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
 
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class QueryParamsTest {
@@ -162,14 +168,22 @@ public class QueryParamsTest {
         assertNotNull(params.fields());
 
         // fields for write filtering
-        final var containerSchema = mock(ContainerSchemaNode.class);
-        doReturn(QName.create(containerChild, "container")).when(containerSchema).getQName();
+        final var containerSchema = mock(ContainerSchemaNode.class,
+            withSettings().extraInterfaces(ContainerEffectiveStatement.class));
+        final var containerQName = QName.create(containerChild, "container");
+        doReturn(containerQName).when(containerSchema).getQName();
+        doReturn(SchemaPath.create(true, containerQName)).when(containerSchema).getPath();
         final var containerChildSchema = mock(LeafSchemaNode.class);
         doReturn(containerChild).when(containerChildSchema).getQName();
         doReturn(containerChildSchema).when(containerSchema).dataChildByName(containerChild);
 
+        final var module = mock(ModuleEffectiveStatement.class);
+        doReturn(Optional.of(containerSchema)).when(module).findSchemaTreeNode(containerQName);
+        final var context = mock(EffectiveModelContext.class);
+        doReturn(Map.of(containerQName.getModule(), module)).when(context).getModuleStatements();
+
         final QueryParameters queryParameters = QueryParams.newQueryParameters(params,
-            InstanceIdentifierContext.ofDataSchemaNode(mock(EffectiveModelContext.class), containerSchema));
+            InstanceIdentifierContext.ofDataSchemaNode(context, containerSchema));
         final List<Set<QName>> fields = queryParameters.fields();
         assertNotNull(fields);
         assertEquals(1, fields.size());
index f1c79df595b15794bd1c57a452b576b8750f879a..d428819833619b98612c9d07656ee4fc41d25aa4 100644 (file)
@@ -123,7 +123,7 @@ public class CreateStreamUtilTest {
                 .withValue(o).build();
         container.withChild(lfNode);
 
-        return NormalizedNodePayload.of(new InstanceIdentifierContext(null, rpcInputSchemaNode, null, SCHEMA_CTX),
-                container.build());
+        return NormalizedNodePayload.of(
+            InstanceIdentifierContext.ofDataSchemaNode(SCHEMA_CTX, rpcInputSchemaNode, null), container.build());
     }
 }
index ce2ab22c716718799459533fc2cbb5173c2c3a22..b959cf70a7a40554c78dd91d4d997e8a49aeafb3 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
 
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
@@ -16,7 +18,6 @@ import static org.mockito.Mockito.mock;
 
 import com.google.common.util.concurrent.Futures;
 import java.io.FileNotFoundException;
-import java.util.Optional;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.junit.MockitoJUnitRunner;
@@ -37,9 +38,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
-import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
@@ -75,9 +74,15 @@ public class Netconf799Test {
             mock(DOMMountPointService.class), mock(RestconfStreamsSubscriptionService.class),
             actionService, mock(Configuration.class));
 
-        final var schemaNode = loadAction(contextRef, RESET_QNAME, ACTION_YII).orElseThrow();
+        final var nodeAndStack = DataSchemaContextTree.from(contextRef).enterPath(ACTION_YII).orElseThrow();
+        final var node = nodeAndStack.node().getDataSchemaNode();
+        assertThat(node, instanceOf(ActionNodeContainer.class));
+        final var actionNode = ((ActionNodeContainer) node).findAction(RESET_QNAME).orElseThrow();
+        final var stack = nodeAndStack.stack();
+        stack.enterSchemaTree(RESET_QNAME);
+
         final var response = dataService.invokeAction(NormalizedNodePayload.of(
-            new InstanceIdentifierContext(ACTION_YII, schemaNode, null, contextRef),
+            InstanceIdentifierContext.ofAction(stack, actionNode, ACTION_YII, null),
             Builders.containerBuilder()
                 .withNodeIdentifier(NodeIdentifier.create(INPUT_QNAME))
                 .withChild(ImmutableNodes.leafNode(DELAY_QNAME, Uint32.TEN))
@@ -86,13 +91,4 @@ public class Netconf799Test {
         assertEquals(204, response.getStatus());
         assertNull(response.getEntity());
     }
-
-    private static Optional<ActionDefinition> loadAction(final EffectiveModelContext modelContext,
-            final QName actionQName, final YangInstanceIdentifier actionYII) {
-        return DataSchemaContextTree.from(modelContext)
-            .findChild(actionYII)
-            .map(DataSchemaContextNode::getDataSchemaNode)
-            .flatMap(node -> node instanceof ActionNodeContainer ? ((ActionNodeContainer) node).findAction(actionQName)
-                : Optional.empty());
-    }
 }
index 33d1a85d7e46d1c2b5b539073845b3cbc658d006..e18c8f4e820d2a7b084f963860a4e205b0720c52 100644 (file)
@@ -179,14 +179,16 @@ public class RestconfInvokeOperationsServiceImplTest {
     }
 
     private NormalizedNodePayload prepNNC(final NormalizedNode result) {
-        final RpcDefinition schemaNode = mock(RpcDefinition.class);
-        final QName qname = QName.create("invoke:rpc:module", "2013-12-03", "rpcTest");
-        doReturn(qname).when(schemaNode).getQName();
+        final QName qname = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test");
+        final RpcDefinition schemaNode = CONTEXT.getOperations().stream().filter(rpc -> rpc.getQName().equals(qname))
+            .findFirst()
+            .orElseThrow();
+
         final NormalizedNode data = mock(NormalizedNode.class);
         final DOMRpcResult domRpcResult = mock(DOMRpcResult.class);
         doReturn(immediateFluentFuture(domRpcResult)).when(rpcService).invokeRpc(qname, data);
         doReturn(result).when(domRpcResult).getResult();
         return NormalizedNodePayload.of(
-            InstanceIdentifierContext.ofLocalRpc(CONTEXT, schemaNode), data);
+            InstanceIdentifierContext.ofRpcInput(CONTEXT, schemaNode, null), data);
     }
 }
index 50a62a405929107572a329c1764cdf92454e72d5..96a31e827511b36c550a6e10b088711d05791486 100644 (file)
@@ -10,16 +10,12 @@ package org.opendaylight.restconf.nb.rfc8040.utils.parser;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.when;
 
 import java.text.ParseException;
 import java.util.List;
-import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mock;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.FieldsParam;
@@ -30,12 +26,7 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public abstract class AbstractFieldsTranslatorTest<T> {
@@ -52,157 +43,68 @@ public abstract class AbstractFieldsTranslatorTest<T> {
     // FIXME: remove all this mocking -- just parse the underlying model and be done with it
 
     // container jukebox
-    @Mock
-    private ContainerSchemaNode containerJukebox;
     private static final QName JUKEBOX_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "jukebox");
 
     // container player
-    @Mock
-    private ContainerSchemaNode containerPlayer;
     protected static final QName PLAYER_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "player");
 
     // container library
-    @Mock
-    private ContainerSchemaNode containerLibrary;
     protected static final QName LIBRARY_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "library");
 
+    // list artist
+    protected static final QName ARTIST_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "artist");
+
     // container augmented library
-    @Mock
-    private ContainerSchemaNode augmentedContainerLibrary;
     protected static final QName AUGMENTED_LIBRARY_Q_NAME = QName.create(Q_NAME_MODULE_AUGMENTED_JUKEBOX,
             "augmented-library");
 
-    // augmentation that contains speed leaf
-    @Mock
-    private AugmentationSchemaNode speedAugmentation;
-
     // leaf speed
-    @Mock
-    private LeafSchemaNode leafSpeed;
     protected static final QName SPEED_Q_NAME = QName.create(Q_NAME_MODULE_AUGMENTED_JUKEBOX, "speed");
 
     // list album
-    @Mock
-    private ListSchemaNode listAlbum;
     public static final QName ALBUM_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "album");
 
     // leaf name
-    @Mock
-    private LeafSchemaNode leafName;
     protected static final QName NAME_Q_NAME = QName.create(Q_NAME_MODULE_JUKEBOX, "name");
 
     // container test data
-    @Mock
-    private ContainerSchemaNode containerTestData;
     private static final QName TEST_DATA_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "test-data");
 
     // list services
-    @Mock
-    private ListSchemaNode listServices;
     protected static final QName SERVICES_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "services");
 
     // leaf type-of-service
-    @Mock
-    private LeafSchemaNode leafTypeOfService;
     protected static final QName TYPE_OF_SERVICE_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "type-of-service");
 
     // list instance
-    @Mock
-    private ListSchemaNode listInstance;
     protected static final QName INSTANCE_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "instance");
 
     // leaf instance-name
-    @Mock
-    private LeafSchemaNode leafInstanceName;
     protected static final QName INSTANCE_NAME_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "instance-name");
 
     // leaf provider
-    @Mock
-    private LeafSchemaNode leafProvider;
     protected static final QName PROVIDER_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "provider");
 
     // container next-data
-    @Mock
-    private ContainerSchemaNode containerNextData;
     protected static final QName NEXT_DATA_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "next-data");
 
     // leaf next-service
-    @Mock
-    private LeafSchemaNode leafNextService;
     protected static final QName NEXT_SERVICE_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "next-service");
 
     // leaf-list protocols
-    @Mock
-    private LeafListSchemaNode leafListProtocols;
     protected static final QName PROTOCOLS_Q_NAME = QName.create(Q_NAME_MODULE_TEST_SERVICES, "protocols");
 
     @Before
     public void setUp() throws Exception {
         final EffectiveModelContext schemaContextJukebox =
                 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/jukebox"));
-        initJukeboxSchemaNodes(schemaContextJukebox);
+        identifierJukebox = InstanceIdentifierContext.ofDataSchemaNode(schemaContextJukebox,
+            schemaContextJukebox.getDataChildByName(JUKEBOX_Q_NAME));
 
         final EffectiveModelContext schemaContextTestServices =
                 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/test-services"));
-        initTestServicesSchemaNodes(schemaContextTestServices);
-    }
-
-    private void initJukeboxSchemaNodes(final EffectiveModelContext schemaContext) {
-        identifierJukebox = InstanceIdentifierContext.ofDataSchemaNode(schemaContext, containerJukebox);
-        when(containerJukebox.getQName()).thenReturn(JUKEBOX_Q_NAME);
-
-        when(containerLibrary.getQName()).thenReturn(LIBRARY_Q_NAME);
-        when(containerJukebox.dataChildByName(LIBRARY_Q_NAME)).thenReturn(containerLibrary);
-
-        when(augmentedContainerLibrary.getQName()).thenReturn(AUGMENTED_LIBRARY_Q_NAME);
-        when(containerJukebox.dataChildByName(AUGMENTED_LIBRARY_Q_NAME))
-                .thenReturn(augmentedContainerLibrary);
-
-        when(containerPlayer.getQName()).thenReturn(PLAYER_Q_NAME);
-        when(containerJukebox.dataChildByName(PLAYER_Q_NAME)).thenReturn(containerPlayer);
-
-        when(listAlbum.getQName()).thenReturn(ALBUM_Q_NAME);
-        when(containerLibrary.dataChildByName(ALBUM_Q_NAME)).thenReturn(listAlbum);
-
-        when(leafName.getQName()).thenReturn(NAME_Q_NAME);
-        when(listAlbum.dataChildByName(NAME_Q_NAME)).thenReturn(leafName);
-
-        when(leafSpeed.getQName()).thenReturn(SPEED_Q_NAME);
-        when(leafSpeed.isAugmenting()).thenReturn(true);
-        when(containerPlayer.dataChildByName(SPEED_Q_NAME)).thenReturn(leafSpeed);
-        when(containerPlayer.getDataChildByName(SPEED_Q_NAME)).thenReturn(leafSpeed);
-        doReturn(List.of(leafSpeed)).when(speedAugmentation).getChildNodes();
-        doReturn(List.of(speedAugmentation)).when(containerPlayer).getAvailableAugmentations();
-        when(speedAugmentation.findDataChildByName(SPEED_Q_NAME)).thenReturn(Optional.of(leafSpeed));
-    }
-
-    private void initTestServicesSchemaNodes(final EffectiveModelContext schemaContext) {
-        identifierTestServices = InstanceIdentifierContext.ofDataSchemaNode(schemaContext, containerTestData);
-        when(containerTestData.getQName()).thenReturn(TEST_DATA_Q_NAME);
-
-        when(listServices.getQName()).thenReturn(SERVICES_Q_NAME);
-        when(containerTestData.dataChildByName(SERVICES_Q_NAME)).thenReturn(listServices);
-
-        when(leafListProtocols.getQName()).thenReturn(PROTOCOLS_Q_NAME);
-        when(containerTestData.dataChildByName(PROTOCOLS_Q_NAME)).thenReturn(leafListProtocols);
-
-        when(leafTypeOfService.getQName()).thenReturn(TYPE_OF_SERVICE_Q_NAME);
-        when(listServices.dataChildByName(TYPE_OF_SERVICE_Q_NAME)).thenReturn(leafTypeOfService);
-
-        when(listInstance.getQName()).thenReturn(INSTANCE_Q_NAME);
-        when(listServices.dataChildByName(INSTANCE_Q_NAME)).thenReturn(listInstance);
-
-        when(leafInstanceName.getQName()).thenReturn(INSTANCE_NAME_Q_NAME);
-        when(listInstance.dataChildByName(INSTANCE_NAME_Q_NAME)).thenReturn(leafInstanceName);
-
-        when(leafProvider.getQName()).thenReturn(PROVIDER_Q_NAME);
-        when(listInstance.dataChildByName(PROVIDER_Q_NAME)).thenReturn(leafProvider);
-
-        when(containerNextData.getQName()).thenReturn(NEXT_DATA_Q_NAME);
-        when(listServices.dataChildByName(NEXT_DATA_Q_NAME)).thenReturn(containerNextData);
-
-        when(leafNextService.getQName()).thenReturn(NEXT_SERVICE_Q_NAME);
-        when(containerNextData.dataChildByName(NEXT_SERVICE_Q_NAME)).thenReturn(leafNextService);
+        identifierTestServices = InstanceIdentifierContext.ofDataSchemaNode(schemaContextTestServices,
+            schemaContextTestServices.getDataChildByName(TEST_DATA_Q_NAME));
     }
 
     protected abstract List<T> translateFields(InstanceIdentifierContext context, FieldsParam fields);
@@ -236,7 +138,7 @@ public abstract class AbstractFieldsTranslatorTest<T> {
      */
     @Test
     public void testSubPath() {
-        final var result = translateFields(identifierJukebox, assertFields("library/album/name"));
+        final var result = translateFields(identifierJukebox, assertFields("library/artist/album/name"));
         assertNotNull(result);
         assertSubPath(result);
     }
@@ -248,7 +150,7 @@ public abstract class AbstractFieldsTranslatorTest<T> {
      */
     @Test
     public void testChildrenPath() {
-        final var result = translateFields(identifierJukebox, assertFields("library(album(name))"));
+        final var result = translateFields(identifierJukebox, assertFields("library(artist(album(name)))"));
         assertNotNull(result);
         assertChildrenPath(result);
     }
index cbea2c49ea71be0bb221155e03ccd752079e3fbf..137ae44c8ea78136353e7dbac5fc1d75fb0e9a66 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 
 /**
  * Unit test for {@link NetconfFieldsTranslator}.
@@ -61,10 +62,11 @@ public class NetconfFieldsTranslatorTest extends AbstractFieldsTranslatorTest<Ya
     protected void assertChildrenPath(final List<YangInstanceIdentifier> result) {
         assertEquals(1, result.size());
         final var pathArguments = result.get(0).getPathArguments();
-        assertEquals(3, pathArguments.size());
+        assertEquals(4, pathArguments.size());
         assertEquals(LIBRARY_Q_NAME, pathArguments.get(0).getNodeType());
-        assertEquals(ALBUM_Q_NAME, pathArguments.get(1).getNodeType());
-        assertEquals(NAME_Q_NAME, pathArguments.get(2).getNodeType());
+        assertEquals(ARTIST_Q_NAME, pathArguments.get(1).getNodeType());
+        assertEquals(ALBUM_Q_NAME, pathArguments.get(2).getNodeType());
+        assertEquals(NAME_Q_NAME, pathArguments.get(3).getNodeType());
     }
 
     @Override
@@ -110,7 +112,10 @@ public class NetconfFieldsTranslatorTest extends AbstractFieldsTranslatorTest<Ya
     @Override
     protected void assertListFieldUnderList(final List<YangInstanceIdentifier> result) {
         assertEquals(1, result.size());
-        assertEquals(List.of(new NodeIdentifier(SERVICES_Q_NAME), new NodeIdentifier(INSTANCE_Q_NAME)),
+        assertEquals(List.of(
+            // FIXME: this does not look right: where are the corresponding NodeIdentifiers?
+            NodeIdentifierWithPredicates.of(SERVICES_Q_NAME),
+            NodeIdentifierWithPredicates.of(INSTANCE_Q_NAME)),
             result.get(0).getPathArguments());
     }
 
index d809bedab7e9168c4bd931385c3f001fb123355f..d1f5a7ef2e155c426a1e8d4812e02efde24b0453 100644 (file)
@@ -42,18 +42,20 @@ public class WriterFieldsTranslatorTest extends AbstractFieldsTranslatorTest<Set
 
     @Override
     protected void assertSubPath(final List<Set<QName>> result) {
-        assertEquals(3, result.size());
+        assertEquals(4, result.size());
         assertEquals(Set.of(LIBRARY_Q_NAME), result.get(0));
-        assertEquals(Set.of(ALBUM_Q_NAME), result.get(1));
-        assertEquals(Set.of(NAME_Q_NAME), result.get(2));
+        assertEquals(Set.of(ARTIST_Q_NAME), result.get(1));
+        assertEquals(Set.of(ALBUM_Q_NAME), result.get(2));
+        assertEquals(Set.of(NAME_Q_NAME), result.get(3));
     }
 
     @Override
     protected void assertChildrenPath(final List<Set<QName>> result) {
-        assertEquals(3, result.size());
+        assertEquals(4, result.size());
         assertEquals(Set.of(LIBRARY_Q_NAME), result.get(0));
-        assertEquals(Set.of(ALBUM_Q_NAME), result.get(1));
-        assertEquals(Set.of(NAME_Q_NAME), result.get(2));
+        assertEquals(Set.of(ARTIST_Q_NAME), result.get(1));
+        assertEquals(Set.of(ALBUM_Q_NAME), result.get(2));
+        assertEquals(Set.of(NAME_Q_NAME), result.get(3));
     }
 
     @Override
index 9a44c4c91433775085829f3d64c3bd911085685d..5bf0fb1c4961409e7fedf94f39d06d0c4648780d 100644 (file)
@@ -69,7 +69,8 @@ public class YangInstanceIdentifierDeserializerTest {
      */
     @Test
     public void deserializeContainerTest() {
-        final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA");
+        final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA")
+            .path.getPathArguments();
         assertEquals(1, result.size());
         assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
     }
@@ -80,7 +81,8 @@ public class YangInstanceIdentifierDeserializerTest {
      */
     @Test
     public void deserializeContainerWithLeafTest() {
-        final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A");
+        final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A")
+            .path.getPathArguments();
         assertEquals(2, result.size());
         assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
         assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")), result.get(1));
@@ -93,7 +95,7 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializeContainerWithListWithLeafListTest() {
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "deserializer-test:contA/list-A=100/leaf-list-AA=instance");
+            "deserializer-test:contA/list-A=100/leaf-list-AA=instance").path.getPathArguments();
         assertEquals(5, result.size());
 
         // container
@@ -116,7 +118,7 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializeContainerWithListWithActionTest() {
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "example-actions:interfaces/interface=eth0/reset");
+            "example-actions:interfaces/interface=eth0/reset").path.getPathArguments();
         assertEquals(4, result.size());
         // container
         assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0));
@@ -135,7 +137,7 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializeContainerWithChoiceSchemaNodeWithActionTest() {
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "example-actions:interfaces/typeA-gigabyte/interface=eth0/reboot");
+            "example-actions:interfaces/typeA-gigabyte/interface=eth0/reboot").path.getPathArguments();
         assertEquals(6, result.size());
 
         // container
@@ -161,7 +163,7 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializeContainerWithChoiceCaseSchemaNodeWithActionTest() {
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "example-actions:interfaces/udp/reboot");
+            "example-actions:interfaces/udp/reboot").path.getPathArguments();
         assertEquals(4, result.size());
         // container
         assertEquals(NodeIdentifier.create(ACTIONS_INTERFACES), result.get(0));
@@ -179,7 +181,8 @@ public class YangInstanceIdentifierDeserializerTest {
      */
     @Test
     public void deserializeListWithNoKeysTest() {
-        final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key");
+        final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key")
+            .path.getPathArguments();
         assertEquals(2, result.size());
         final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
         assertEquals(NodeIdentifier.create(list), result.get(0));
@@ -193,7 +196,7 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializeListWithOneKeyTest() {
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "deserializer-test:list-one-key=value");
+            "deserializer-test:list-one-key=value").path.getPathArguments();
         assertEquals(2, result.size());
         final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
         assertEquals(NodeIdentifier.create(list), result.get(0));
@@ -213,7 +216,7 @@ public class YangInstanceIdentifierDeserializerTest {
             QName.create(list, "enabled"), false);
 
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "deserializer-test:list-multiple-keys=value,100,false");
+            "deserializer-test:list-multiple-keys=value,100,false").path.getPathArguments();
         assertEquals(2, result.size());
         assertEquals(NodeIdentifier.create(list), result.get(0));
         assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
@@ -226,7 +229,7 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializeLeafListTest() {
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "deserializer-test:leaf-list-0=true");
+            "deserializer-test:leaf-list-0=true").path.getPathArguments();
         assertEquals(2, result.size());
 
         final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
@@ -239,7 +242,7 @@ public class YangInstanceIdentifierDeserializerTest {
      */
     @Test
     public void deserializeEmptyDataTest() {
-        assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, ""));
+        assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "").path.getPathArguments());
     }
 
     /**
@@ -249,7 +252,7 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializeMultipleSlashesTest() throws ParseException {
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            ApiPath.parseUrl("deserializer-test:contA////list-A=40//list-key"));
+            ApiPath.parseUrl("deserializer-test:contA////list-A=40//list-key")).path.getPathArguments();
         assertEquals(4, result.size());
 
         // container
@@ -561,7 +564,7 @@ public class YangInstanceIdentifierDeserializerTest {
             QName.create(list, "enabled"), false);
 
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "deserializer-test:list-multiple-keys=%3Afoo,1,false/string-value");
+            "deserializer-test:list-multiple-keys=%3Afoo,1,false/string-value").path.getPathArguments();
         assertEquals(3, result.size());
         // list
         assertEquals(NodeIdentifier.create(list), result.get(0));
@@ -592,8 +595,7 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
         final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
-        final YangInstanceIdentifier result = YangInstanceIdentifier.create(
-                YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI));
+        final YangInstanceIdentifier result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI).path;
 
         final Iterator<Entry<QName, Object>> resultListKeys =
                 ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
@@ -615,7 +617,7 @@ public class YangInstanceIdentifierDeserializerTest {
             QName.create(list, "enabled"), true);
 
         final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "deserializer-test:list-multiple-keys=,0,true");
+            "deserializer-test:list-multiple-keys=,0,true").path.getPathArguments();
         assertEquals(2, result.size());
         assertEquals(NodeIdentifier.create(list), result.get(0));
         assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
@@ -640,7 +642,8 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializePartInOtherModuleTest() {
         final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "deserializer-test-included:augmented-list=100/deserializer-test:augmented-leaf");
+            "deserializer-test-included:augmented-list=100/deserializer-test:augmented-leaf")
+            .path.getPathArguments();
         assertEquals(4, result.size());
 
         // list
@@ -659,7 +662,8 @@ public class YangInstanceIdentifierDeserializerTest {
     @Test
     public void deserializeListInOtherModuleTest() {
         final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
-            "deserializer-test-included:augmented-list=100/deserializer-test:augmenting-list=0");
+            "deserializer-test-included:augmented-list=100/deserializer-test:augmenting-list=0")
+            .path.getPathArguments();
         assertEquals(5, result.size());
 
         // list
@@ -695,7 +699,7 @@ public class YangInstanceIdentifierDeserializerTest {
     }
 
     private static void assertIdentityrefKeyValue(final String path) {
-        final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, path);
+        final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, path).path.getPathArguments();
         assertEquals(4, pathArgs.size());
 
         assertEquals("refs", pathArgs.get(0).getNodeType().getLocalName());