Fix LeafRefContext for relative refs defined in external grouping 30/74630/2
authorMarek Gradzki <mgradzki@cisco.com>
Mon, 30 Jul 2018 06:45:29 +0000 (08:45 +0200)
committerRobert Varga <nite@hq.sk>
Thu, 2 Aug 2018 08:49:14 +0000 (08:49 +0000)
LeafRefPathParserListenerImpl builds LeafRefPath
using namespace of the module which contains leafref definition.

While it is valid approach for absolute paths with prefix (YANGTOOLS-749),
it cannot be used with groupings, because they are bound to namespace
when contents of the grouping is added to the schema tree.

This patch fixes the relative path case
by using the module which uses leafref.

JIRA: YANGTOOLS-891
Change-Id: Ia903138f80c437937374eb7d3460fdbe8b2a97c2
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools891Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/bar.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/baz-top.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/baz.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-invalid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-valid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-invalid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-valid.xml [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserListenerImpl.java

diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools891Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools891Test.java
new file mode 100644 (file)
index 0000000..1d17433
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.xml;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidatation;
+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.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Yangtools891Test {
+    private static final String FOO = "foo";
+    private static final String FOO_NS = "urn:opendaylight:params:xml:ns:yang:foo";
+    private static final String FOO_REV = "2018-07-27";
+    private static final QName FOO_TOP = QName.create(FOO_NS, FOO_REV, "foo-top");
+    private static final YangInstanceIdentifier FOO_TOP_ID = YangInstanceIdentifier.of(FOO_TOP);
+    private static final String BAZ = "baz";
+    private static final String BAZ_NS = "urn:opendaylight:params:xml:ns:yang:baz";
+    private static final String BAZ_REV = "2018-07-27";
+    private static final QName BAZ_TOP = QName.create(BAZ_NS, BAZ_REV, "baz-top");
+    private static final YangInstanceIdentifier BAZ_TOP_ID = YangInstanceIdentifier.of(BAZ_TOP);
+
+    private SchemaContext schemaContext;
+    private LeafRefContext leafRefContext;
+    private DataTree dataTree;
+    private ContainerSchemaNode fooTopNode;
+    private ContainerSchemaNode bazTopNode;
+
+    @Before
+    public void setup() {
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/yangtools891");
+        leafRefContext = LeafRefContext.create(schemaContext);
+        dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+        final Module fooModule = schemaContext.findModule(FOO, Revision.of(FOO_REV)).get();
+        fooTopNode = (ContainerSchemaNode) fooModule.findDataChildByName(FOO_TOP).get();
+        final Module bazModule = schemaContext.findModule(BAZ, Revision.of(BAZ_REV)).get();
+        bazTopNode = (ContainerSchemaNode) bazModule.findDataChildByName(BAZ_TOP).get();
+    }
+
+    @Test
+    public void testValid() throws Exception {
+        final NormalizedNode<?, ?> fooTop = readNode("/yangtools891/grouping-with-list-valid.xml", fooTopNode);
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(FOO_TOP_ID, fooTop);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidatation.validate(writeContributorsCandidate, leafRefContext);
+        dataTree.commit(writeContributorsCandidate);
+    }
+
+    @Test(expected = LeafRefDataValidationFailedException.class)
+    public void testInvalid() throws Exception {
+        final NormalizedNode<?, ?> fooTop = readNode("/yangtools891/grouping-with-list-invalid.xml", fooTopNode);
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(FOO_TOP_ID, fooTop);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidatation.validate(writeContributorsCandidate, leafRefContext);
+    }
+
+    @Test
+    public void testGroupingWithLeafrefValid() throws Exception {
+        final NormalizedNode<?, ?> bazTop = readNode("/yangtools891/baz-top.xml", bazTopNode);
+        final NormalizedNode<?, ?> fooTop = readNode("/yangtools891/grouping-with-leafref-valid.xml", fooTopNode);
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(BAZ_TOP_ID, bazTop);
+        writeModification.write(FOO_TOP_ID, fooTop);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidatation.validate(writeContributorsCandidate, leafRefContext);
+    }
+
+    @Test(expected = LeafRefDataValidationFailedException.class)
+    public void testGroupingWithLeafrefInvalid() throws Exception {
+        final NormalizedNode<?, ?> bazTop = readNode("/yangtools891/baz-top.xml", bazTopNode);
+        final NormalizedNode<?, ?> fooTop = readNode("/yangtools891/grouping-with-leafref-invalid.xml", fooTopNode);
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(BAZ_TOP_ID, bazTop);
+        writeModification.write(FOO_TOP_ID, fooTop);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidatation.validate(writeContributorsCandidate, leafRefContext);
+    }
+
+    private NormalizedNode<?, ?> readNode(final String filename, final ContainerSchemaNode node) throws Exception {
+        final InputStream resourceAsStream = Yangtools891Test.class.getResourceAsStream(filename);
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, node);
+        xmlParser.parse(reader);
+        return result.getResult();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/bar.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools891/bar.yang
new file mode 100644 (file)
index 0000000..2dac68e
--- /dev/null
@@ -0,0 +1,22 @@
+module bar {
+    namespace "urn:opendaylight:params:xml:ns:yang:bar";
+    prefix bar;
+
+    revision 2018-07-27;
+
+    grouping grouping-with-list {
+        list list-in-grouping {
+            key "name";
+            leaf name {
+                type leafref {
+                    path "../container-in-list/name";
+                }
+            }
+            container container-in-list {
+                leaf name {
+                    type string;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz-top.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz-top.xml
new file mode 100644 (file)
index 0000000..762cd8e
--- /dev/null
@@ -0,0 +1,5 @@
+<top-container xmlns="urn:opendaylight:params:xml:ns:yang:baz">
+    <list-in-container>
+        <name>name1</name>
+    </list-in-container>
+</top-container>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz.yang
new file mode 100644 (file)
index 0000000..8ee5d00
--- /dev/null
@@ -0,0 +1,23 @@
+module baz {
+    namespace "urn:opendaylight:params:xml:ns:yang:baz";
+    prefix baz;
+
+    revision 2018-07-27;
+
+    grouping grouping-with-leafref {
+        leaf ref {
+            type leafref {
+                path "/baz-top/list-in-container/name";
+            }
+        }
+    }
+
+    container baz-top {
+        list list-in-container {
+            key "name";
+            leaf name {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/foo.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools891/foo.yang
new file mode 100644 (file)
index 0000000..35207e5
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace "urn:opendaylight:params:xml:ns:yang:foo";
+    prefix foo;
+
+    import bar {
+        prefix bar;
+    }
+    import baz {
+        prefix baz;
+    }
+
+    revision 2018-07-27;
+
+    container foo-top {
+        uses bar:grouping-with-list;
+        uses baz:grouping-with-leafref;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-invalid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-invalid.xml
new file mode 100644 (file)
index 0000000..563bce5
--- /dev/null
@@ -0,0 +1,3 @@
+<root xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <ref>name3</ref>
+</root>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-valid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-valid.xml
new file mode 100644 (file)
index 0000000..12aa972
--- /dev/null
@@ -0,0 +1,3 @@
+<root xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <ref>name1</ref>
+</root>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-invalid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-invalid.xml
new file mode 100644 (file)
index 0000000..e4ab91b
--- /dev/null
@@ -0,0 +1,8 @@
+<foo-top xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <list-in-grouping>
+        <name>name1</name>
+        <container-in-list>
+            <name>name2</name>
+        </container-in-list>
+    </list-in-grouping>
+</foo-top>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-valid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-valid.xml
new file mode 100644 (file)
index 0000000..b0412ba
--- /dev/null
@@ -0,0 +1,8 @@
+<foo-top xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <list-in-grouping>
+        <name>name1</name>
+        <container-in-list>
+            <name>name1</name>
+        </container-in-list>
+    </list-in-grouping>
+</foo-top>
index bf4612d01f6e37ec642d58bd0c082841fd960ded..5356a59609d3cd48129eb6789146b1afbca8ae36 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.yangtools.yang.data.impl.leafref;
 import static com.google.common.base.Preconditions.checkArgument;
 
 import com.google.common.collect.Lists;
-import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -36,6 +35,7 @@ final class LeafRefPathParserListenerImpl extends LeafRefPathParserBaseListener
     private final List<QNameWithPredicateBuilder> leafRefPathQnameList = new ArrayList<>();
     private final SchemaContext schemaContext;
     private final Module module;
+    private final Module leafrefModule;
     // FIXME: use for identifier path completion
     private final SchemaNode node;
 
@@ -52,10 +52,11 @@ final class LeafRefPathParserListenerImpl extends LeafRefPathParserBaseListener
         LEAF_REF_PATH, PATH_PREDICATE, PREDICATE_PATH_EQUALITY_EXPR, PATH_KEY_EXPR
     }
 
-    LeafRefPathParserListenerImpl(final SchemaContext schemaContext, final Module currentModule,
+    LeafRefPathParserListenerImpl(final SchemaContext schemaContext, final Module leafrefModule,
             final SchemaNode currentNode) {
         this.schemaContext = schemaContext;
-        this.module = currentModule;
+        this.module = schemaContext.findModule(currentNode.getQName().getModule()).get();
+        this.leafrefModule = leafrefModule;
         this.node = currentNode;
     }
 
@@ -115,13 +116,13 @@ final class LeafRefPathParserListenerImpl extends LeafRefPathParserBaseListener
     @Override
     public void enterPrefix(final PrefixContext ctx) {
         final String prefix = ctx.getText();
-        if (!module.getPrefix().equals(prefix)) {
-            final Optional<QNameModule> qnameModuleOpt = getQNameModuleForImportPrefix(prefix);
+        if (!leafrefModule.getPrefix().equals(prefix)) {
+            final Optional<QNameModule> qnameModuleOpt = getQNameModuleForImportPrefix(leafrefModule, prefix);
             checkArgument(qnameModuleOpt.isPresent(), "No module import for prefix: %s in module: %s", prefix,
-                module.getName());
+                leafrefModule.getName());
             currentQnameModule = qnameModuleOpt.get();
         } else {
-            currentQnameModule = module.getQNameModule();
+            currentQnameModule = leafrefModule.getQNameModule();
         }
     }
 
@@ -139,7 +140,11 @@ final class LeafRefPathParserListenerImpl extends LeafRefPathParserBaseListener
     @Override
     public void exitNode_identifier(final Node_identifierContext ctx) {
         if (currentQnameModule == null) {
-            currentQnameModule = module.getQNameModule();
+            if (relativePath) {
+                currentQnameModule = module.getQNameModule();
+            } else {
+                currentQnameModule = leafrefModule.getQNameModule();
+            }
         }
 
         if (currentParsingState == ParsingState.PREDICATE_PATH_EQUALITY_EXPR) {
@@ -163,13 +168,8 @@ final class LeafRefPathParserListenerImpl extends LeafRefPathParserBaseListener
         return leafRefPath;
     }
 
-    private URI getNamespaceForImportPrefix(final String prefix) {
-        final ModuleImport moduleImport = getModuleImport(prefix);
-        return schemaContext.findModule(moduleImport.getModuleName(), moduleImport.getRevision()).get().getNamespace();
-    }
-
-    private Optional<QNameModule> getQNameModuleForImportPrefix(final String prefix) {
-        final ModuleImport moduleImport = getModuleImport(prefix);
+    private Optional<QNameModule> getQNameModuleForImportPrefix(final Module targetModule, final String prefix) {
+        final ModuleImport moduleImport = getModuleImport(targetModule, prefix);
         if (moduleImport == null) {
             return Optional.empty();
         }
@@ -179,7 +179,8 @@ final class LeafRefPathParserListenerImpl extends LeafRefPathParserBaseListener
         return schemaContext.findModule(moduleName, revision).map(Module::getQNameModule);
     }
 
-    private ModuleImport getModuleImport(final String prefix) {
-        return module.getImports().stream().filter(imp -> prefix.equals(imp.getPrefix())).findFirst().orElse(null);
+    private ModuleImport getModuleImport(final Module targetModule, final String prefix) {
+        return targetModule.getImports().stream()
+            .filter(imp -> prefix.equals(imp.getPrefix())).findFirst().orElse(null);
     }
 }