<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-test-util</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-codec-xml</artifactId>
+ </dependency>
</dependencies>
</project>
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.xml.namespace.NamespaceContext;
import org.opendaylight.controller.config.util.xml.DocumentedException;
import org.opendaylight.controller.config.util.xml.MissingNameSpaceException;
import org.opendaylight.controller.config.util.xml.XmlElement;
import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.codec.xml.XmlCodecFactory;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.util.codec.TypeAwareCodec;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+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.type.IdentityrefTypeDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
/**
* Class validates filter content against schema context.
/**
* Validates filter content against this validator schema context. If the filter is valid,
* method returns {@link YangInstanceIdentifier} of node which can be used as root for data selection.
+ *
* @param filterContent filter content
* @return YangInstanceIdentifier
* @throws DocumentedException if filter content is not valid
/**
* Returns module's child data node of given name space and name
- * @param module module
+ *
+ * @param module module
* @param nameSpace name space
- * @param name name
+ * @param name name
* @return child data node schema
* @throws DocumentedException if child with given name is not present
*/
/**
* Recursively checks filter elements against the schema. Returns tree of nodes QNames as they appear in filter.
- * @param element element to check
+ *
+ * @param element element to check
* @param parentNodeSchema parent node schema
- * @param tree parent node tree
+ * @param tree parent node tree
* @return tree
* @throws ValidationException if filter content is not valid
*/
* Searches for YangInstanceIdentifier of node, which can be used as root for data selection.
* It goes as deep in tree as possible. Method stops traversing, when there are multiple child elements. If element
* represents list and child elements are key values, then it builds YangInstanceIdentifier of list entry.
- * @param tree QName tree
+ *
+ * @param tree QName tree
* @param filterContent filter element
- * @param builder builder @return YangInstanceIdentifier
+ * @param builder builder @return YangInstanceIdentifier
*/
private YangInstanceIdentifier getFilterDataRoot(FilterTree tree, final XmlElement filterContent,
final InstanceIdentifierBuilder builder) {
final InstanceIdentifierBuilder builder) {
Preconditions.checkArgument(tree.getSchemaNode() instanceof ListSchemaNode);
final ListSchemaNode listSchemaNode = (ListSchemaNode) tree.getSchemaNode();
- final List<QName> keyDefinition = listSchemaNode.getKeyDefinition();
- final Map<QName, Object> map = getKeyValues(pathToList, filterContent, keyDefinition);
+
+ final Map<QName, Object> map = getKeyValues(pathToList, filterContent, listSchemaNode);
if (!map.isEmpty()) {
builder.nodeWithKey(tree.getName(), map);
}
}
private Map<QName, Object> getKeyValues(final List<String> path, final XmlElement filterContent,
- final List<QName> keyDefinition) {
+ final ListSchemaNode listSchemaNode) {
XmlElement current = filterContent;
//find list element
for (final String pathElement : path) {
current = childElements.get(0);
}
final Map<QName, Object> keys = new HashMap<>();
+ final List<QName> keyDefinition = listSchemaNode.getKeyDefinition();
for (final QName qName : keyDefinition) {
final Optional<XmlElement> childElements = current.getOnlyChildElementOptionally(qName.getLocalName());
if (!childElements.isPresent()) {
}
final Optional<String> keyValue = childElements.get().getOnlyTextContentOptionally();
if (keyValue.isPresent()) {
- keys.put(qName, keyValue.get());
+ final LeafSchemaNode listKey = (LeafSchemaNode) listSchemaNode.getDataChildByName(qName);
+ if (listKey instanceof IdentityrefTypeDefinition) {
+ keys.put(qName, keyValue.get());
+ } else {
+ if (listKey.getType() instanceof IdentityrefTypeDefinition) {
+ final Document document = filterContent.getDomElement().getOwnerDocument();
+ final NamespaceContext nsContext = new UniversalNamespaceContextImpl(document, false);
+ final XmlCodecFactory xmlCodecFactory = XmlCodecFactory.create(schemaContext.getCurrentContext());
+ final TypeAwareCodec identityrefTypeCodec = xmlCodecFactory.codecFor(listKey);
+ final QName deserializedKey = (QName) identityrefTypeCodec.parseValue(nsContext, keyValue.get());
+ keys.put(qName, deserializedKey);
+ } else {
+ final Object deserializedKey = TypeDefinitionAwareCodec.from(listKey.getType())
+ .deserialize(keyValue.get());
+ keys.put(qName, deserializedKey);
+ }
+ }
}
}
return keys;
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.netconf.mdsal.connector.ops.get;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class UniversalNamespaceContextImpl implements NamespaceContext {
+ private static final String DEFAULT_NS = "DEFAULT";
+ private final Map<String, String> prefix2Uri = new HashMap<>();
+ private final Map<String, String> uri2Prefix = new HashMap<>();
+
+ /**
+ * This constructor parses the document and stores all namespaces it can
+ * find. If toplevelOnly is true, only namespaces in the root are used.
+ *
+ * @param document source document
+ * @param toplevelOnly restriction of the search to enhance performance
+ */
+ public UniversalNamespaceContextImpl(final Document document, final boolean toplevelOnly) {
+ readNode(document.getFirstChild(), toplevelOnly);
+ }
+
+ /**
+ * A single node is read, the namespace attributes are extracted and stored.
+ *
+ * @param node to examine
+ * @param attributesOnly, if true no recursion happens
+ */
+ private void readNode(final Node node, final boolean attributesOnly) {
+ final NamedNodeMap attributes = node.getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ final Node attribute = attributes.item(i);
+ storeAttr((Attr) attribute);
+ }
+
+ if (!attributesOnly) {
+ final NodeList chields = node.getChildNodes();
+ for (int i = 0; i < chields.getLength(); i++) {
+ final Node chield = chields.item(i);
+ if (chield.getNodeType() == Node.ELEMENT_NODE)
+ readNode(chield, false);
+ }
+ }
+ }
+
+ /**
+ * This method looks at an attribute and stores it, if it is a namespace
+ * attribute.
+ *
+ * @param attribute to examine
+ */
+ private void storeAttr(final Attr attribute) {
+ // examine the attributes in namespace xmlns
+ if (attribute.getNamespaceURI() != null
+ && attribute.getNamespaceURI().equals(
+ XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
+ // Default namespace xmlns="uri goes here"
+ if (attribute.getNodeName().equals(XMLConstants.XMLNS_ATTRIBUTE)) {
+ putInCache(DEFAULT_NS, attribute.getNodeValue());
+ } else {
+ // The defined prefixes are stored here
+ putInCache(attribute.getLocalName(), attribute.getNodeValue());
+ }
+ }
+
+ }
+
+ private void putInCache(final String prefix, final String uri) {
+ prefix2Uri.put(prefix, uri);
+ uri2Prefix.put(uri, prefix);
+ }
+
+ /**
+ * This method is called by XPath. It returns the default namespace, if the
+ * prefix is null or "".
+ *
+ * @param prefix to search for
+ * @return uri
+ */
+ public String getNamespaceURI(final String prefix) {
+ if (prefix == null || prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
+ return prefix2Uri.get(DEFAULT_NS);
+ } else {
+ return prefix2Uri.get(prefix);
+ }
+ }
+
+ /**
+ * This method is not needed in this context, but can be implemented in a
+ * similar way.
+ */
+ public String getPrefix(final String namespaceURI) {
+ return uri2Prefix.get(namespaceURI);
+ }
+
+ public Iterator getPrefixes(final String namespaceURI) {
+ // Not implemented
+ return null;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.netconf.mdsal.connector.ops.get;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.controller.config.util.xml.XmlElement;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+
+public class Bug8084 {
+
+ private static final QName base = QName.create("urn:dummy:mod-0", "2016-03-01", "mainroot");
+
+ @Test
+ public void testValidateTypes() throws Exception {
+ final List<InputStream> sources = new ArrayList<>();
+ sources.add(getClass().getResourceAsStream("/yang/filter-validator-test-mod-0.yang"));
+ sources.add(getClass().getResourceAsStream("/yang/filter-validator-test-augment.yang"));
+ sources.add(getClass().getResourceAsStream("/yang/mdsal-netconf-mapping-test.yang"));
+ final SchemaContext context = YangParserTestUtils.parseYangStreams(sources);
+ final CurrentSchemaContext currentContext = mock(CurrentSchemaContext.class);
+ doReturn(context).when(currentContext).getCurrentContext();
+ final FilterContentValidator validator = new FilterContentValidator(currentContext);
+
+ final Document document = XmlUtil.readXmlToDocument(FilterContentValidatorTest.class
+ .getResourceAsStream("/filter/bug8084.xml"));
+
+ final XmlElement xmlElement = XmlElement.fromDomDocument(document);
+ final YangInstanceIdentifier actual = validator.validate(xmlElement);
+
+ final Map<QName, Object> inputs = new HashMap<>();
+ inputs.put(QName.create(base, "id1"), "aaa");
+ inputs.put(QName.create(base, "id2"), Byte.valueOf("-9"));
+ inputs.put(QName.create(base, "id3"), Short.valueOf("-30000"));
+ inputs.put(QName.create(base, "id4"), Integer.valueOf("-2000000000"));
+ inputs.put(QName.create(base, "id5"), Long.valueOf("-2000000000000000"));
+ inputs.put(QName.create(base, "id6"), Short.valueOf("9"));
+ inputs.put(QName.create(base, "id7"), Integer.valueOf("30000"));
+ inputs.put(QName.create(base, "id8"), Long.valueOf("2000000000"));
+ inputs.put(QName.create(base, "id9"), BigInteger.valueOf(Long.valueOf("2000000000000000")));
+ inputs.put(QName.create(base, "id10"), true);
+ inputs.put(QName.create(base, "id11"), BigDecimal.valueOf(128.55));
+ inputs.put(QName.create(base, "id12"), QName.create(base, "foo"));
+ inputs.put(QName.create(base, "id13"), QName.create("urn:opendaylight:mdsal:mapping:test", "2015-02-26", "foo"));
+ final QName idActual = (QName) ((YangInstanceIdentifier.NodeIdentifierWithPredicates) actual.getLastPathArgument()).
+ getKeyValues().get(QName.create(base, "id12"));
+
+
+ final YangInstanceIdentifier expected = YangInstanceIdentifier.builder()
+ .node(base)
+ .node(QName.create(base, "multi-key-list2"))
+ .nodeWithKey(QName.create(base, "multi-key-list2"), inputs)
+ .build();
+ final QName idExpected = (QName) ((YangInstanceIdentifier.NodeIdentifierWithPredicates) expected.getLastPathArgument()).
+ getKeyValues().get(QName.create(base, "id12"));
+ assertEquals(idExpected, idActual);
+ assertEquals(expected, actual);
+
+ }
+}
final List<InputStream> sources = new ArrayList<>();
sources.add(getClass().getResourceAsStream("/yang/filter-validator-test-mod-0.yang"));
sources.add(getClass().getResourceAsStream("/yang/filter-validator-test-augment.yang"));
+ sources.add(getClass().getResourceAsStream("/yang/mdsal-netconf-mapping-test.yang"));
final SchemaContext context = YangParserTestUtils.parseYangStreams(sources);
final CurrentSchemaContext currentContext = mock(CurrentSchemaContext.class);
doReturn(context).when(currentContext).getCurrentContext();
--- /dev/null
+<!--
+ ~ Copyright (c) 2017 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
+ -->
+
+<mainroot xmlns="urn:dummy:mod-0">
+ <multi-key-list2>
+ <id1>aaa</id1>
+ <id2>-9</id2>
+ <id3>-30000</id3>
+ <id4>-2000000000</id4>
+ <id5>-2000000000000000</id5>
+ <id6>9</id6>
+ <id7>30000</id7>
+ <id8>2000000000</id8>
+ <id9>2000000000000000</id9>
+ <id10>true</id10>
+ <id11>128.55</id11>
+ <id12>foo</id12>
+ <id13 xmlns:map-test="urn:opendaylight:mdsal:mapping:test">map-test:foo</id13>
+ </multi-key-list2>
+</mainroot>
\ No newline at end of file
namespace "urn:dummy:mod-0";
prefix "mod-0";
revision "2016-03-01";
+
+ import config {
+ prefix map-test;
+ revision-date "2015-02-26";
+ }
+
+ identity foo {
+ description "dummy identity";
+ }
+
container mainroot {
leaf maincontent {
mandatory true;
type string;
}
}
+
+ list multi-key-list2 {
+ key "id1 id2 id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13";
+ leaf id1 {
+ type string;
+ }
+ leaf id2 {
+ type int8;
+ }
+ leaf id3 {
+ type int16;
+ }
+ leaf id4 {
+ type int32;
+ }
+ leaf id5 {
+ type int64;
+ }
+ leaf id6 {
+ type uint8;
+ }
+ leaf id7 {
+ type uint16;
+ }
+ leaf id8 {
+ type uint32;
+ }
+ leaf id9 {
+ type uint64;
+ }
+ leaf id10 {
+ type boolean;
+ }
+ leaf id11 {
+ type decimal64{
+ fraction-digits 2;
+ }
+ }
+ leaf id12 {
+ type identityref {
+ base "foo";
+ }
+ }
+ leaf id13 {
+ type identityref {
+ base "map-test:foo";
+ }
+ }
+ }
}
}
\ No newline at end of file
revision "2015-02-26";
+ identity foo {
+ description "dummy identity";
+ }
container mapping-nodes {
list multiple-keys {