From 6cc3cc053f14daea83992604a49258e4ecc5c012 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Mon, 30 Jul 2018 08:45:29 +0200 Subject: [PATCH] Fix LeafRefContext for relative refs defined in external grouping 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 (cherry picked from commit b1feee95519d1bed1387ba417d7ab06eee86e3f9) --- .../yang/data/codec/xml/Yangtools891Test.java | 122 ++++++++++++++++++ .../src/test/resources/yangtools891/bar.yang | 22 ++++ .../test/resources/yangtools891/baz-top.xml | 5 + .../src/test/resources/yangtools891/baz.yang | 23 ++++ .../src/test/resources/yangtools891/foo.yang | 18 +++ .../grouping-with-leafref-invalid.xml | 3 + .../grouping-with-leafref-valid.xml | 3 + .../grouping-with-list-invalid.xml | 8 ++ .../yangtools891/grouping-with-list-valid.xml | 8 ++ .../LeafRefPathParserListenerImpl.java | 35 ++--- 10 files changed, 230 insertions(+), 17 deletions(-) create mode 100644 yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools891Test.java create mode 100644 yang/yang-data-codec-xml/src/test/resources/yangtools891/bar.yang create mode 100644 yang/yang-data-codec-xml/src/test/resources/yangtools891/baz-top.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/yangtools891/baz.yang create mode 100644 yang/yang-data-codec-xml/src/test/resources/yangtools891/foo.yang create mode 100644 yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-invalid.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-valid.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-invalid.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-valid.xml 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 index 0000000000..1d17433ff9 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools891Test.java @@ -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 index 0000000000..2dac68ea01 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/yangtools891/bar.yang @@ -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 index 0000000000..762cd8ef1b --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz-top.xml @@ -0,0 +1,5 @@ + + + name1 + + 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 index 0000000000..8ee5d00080 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz.yang @@ -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 index 0000000000..35207e5aa1 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/yangtools891/foo.yang @@ -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 index 0000000000..563bce53ea --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-invalid.xml @@ -0,0 +1,3 @@ + + name3 + 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 index 0000000000..12aa972cc1 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-valid.xml @@ -0,0 +1,3 @@ + + name1 + 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 index 0000000000..e4ab91b2de --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-invalid.xml @@ -0,0 +1,8 @@ + + + name1 + + name2 + + + 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 index 0000000000..b0412bacd5 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-valid.xml @@ -0,0 +1,8 @@ + + + name1 + + name1 + + + diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserListenerImpl.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserListenerImpl.java index bf4612d01f..5356a59609 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserListenerImpl.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserListenerImpl.java @@ -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 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 qnameModuleOpt = getQNameModuleForImportPrefix(prefix); + if (!leafrefModule.getPrefix().equals(prefix)) { + final Optional 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 getQNameModuleForImportPrefix(final String prefix) { - final ModuleImport moduleImport = getModuleImport(prefix); + private Optional 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); } } -- 2.36.6