2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.data.codec.xml;
10 import static org.hamcrest.CoreMatchers.containsString;
11 import static org.hamcrest.MatcherAssert.assertThat;
12 import static org.junit.jupiter.api.Assertions.assertEquals;
13 import static org.junit.jupiter.api.Assertions.assertNotNull;
14 import static org.junit.jupiter.api.Assertions.assertThrows;
16 import java.io.IOException;
17 import java.net.URISyntaxException;
18 import java.util.HashMap;
20 import javax.xml.parsers.ParserConfigurationException;
21 import javax.xml.stream.XMLStreamException;
22 import org.junit.jupiter.api.AfterAll;
23 import org.junit.jupiter.api.BeforeAll;
24 import org.junit.jupiter.api.Test;
25 import org.opendaylight.yangtools.util.xml.UntrustedXML;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.QNameModule;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
30 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
36 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
37 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
38 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
39 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
40 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
41 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
42 import org.xml.sax.SAXException;
44 class XmlToNormalizedNodesTest {
45 private static final QName PARENT_CONTAINER = QName.create("foo-namespace", "parent-container");
47 private static final QNameModule BAZ_MODULE = QNameModule.of("baz-namespace");
48 private static final QName OUTER_CONTAINER = QName.create(BAZ_MODULE, "outer-container");
50 private static final QName MY_CONTAINER_1 = QName.create(BAZ_MODULE, "my-container-1");
51 private static final QName MY_KEYED_LIST = QName.create(BAZ_MODULE, "my-keyed-list");
52 private static final QName MY_KEY_LEAF = QName.create(BAZ_MODULE, "my-key-leaf");
53 private static final QName MY_LEAF_IN_LIST_1 = QName.create(BAZ_MODULE, "my-leaf-in-list-1");
54 private static final QName MY_LEAF_IN_LIST_2 = QName.create(BAZ_MODULE, "my-leaf-in-list-2");
55 private static final QName MY_LEAF_1 = QName.create(BAZ_MODULE, "my-leaf-1");
56 private static final QName MY_LEAFLIST = QName.create(BAZ_MODULE, "my-leaf-list");
58 private static final QName MY_CONTAINER_2 = QName.create(BAZ_MODULE, "my-container-2");
59 private static final QName INNER_CONTAINER = QName.create(BAZ_MODULE, "inner-container");
60 private static final QName MY_LEAF_2 = QName.create(BAZ_MODULE, "my-leaf-2");
61 private static final QName MY_LEAF_3 = QName.create(BAZ_MODULE, "my-leaf-3");
62 private static final QName MY_CHOICE = QName.create(BAZ_MODULE, "my-choice");
63 private static final QName MY_LEAF_IN_CASE_2 = QName.create(BAZ_MODULE, "my-leaf-in-case-2");
65 private static final QName MY_CONTAINER_3 = QName.create(BAZ_MODULE, "my-container-3");
66 private static final QName MY_DOUBLY_KEYED_LIST = QName.create(BAZ_MODULE, "my-doubly-keyed-list");
67 private static final QName MY_FIRST_KEY_LEAF = QName.create(BAZ_MODULE, "my-first-key-leaf");
68 private static final QName MY_SECOND_KEY_LEAF = QName.create(BAZ_MODULE, "my-second-key-leaf");
69 private static final QName MY_LEAF_IN_LIST_3 = QName.create(BAZ_MODULE, "my-leaf-in-list-3");
71 private static EffectiveModelContext schemaContext;
72 private static Inference outerContainerSchema;
73 private static Inference parentContainerSchema;
77 schemaContext = YangParserTestUtils.parseYangResources(XmlToNormalizedNodesTest.class,
78 "/foo.yang", "/baz.yang");
79 parentContainerSchema = Inference.ofDataTreePath(schemaContext, PARENT_CONTAINER);
80 outerContainerSchema = Inference.ofDataTreePath(schemaContext, OUTER_CONTAINER);
84 static void cleanup() {
86 parentContainerSchema = null;
87 outerContainerSchema = null;
91 void testComplexXmlParsing() throws IOException, SAXException, URISyntaxException, XMLStreamException,
92 ParserConfigurationException {
93 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/baz.xml");
94 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
95 final var result = new NormalizationResultHolder();
96 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
98 try (var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema)) {
99 xmlParser.parse(reader);
102 final NormalizedNode transformedInput = result.getResult().data();
103 assertNotNull(transformedInput);
105 final NormalizedNode expectedNormalizedNode = buildOuterContainerNode();
106 assertNotNull(expectedNormalizedNode);
108 assertEquals(expectedNormalizedNode, transformedInput);
112 void testSimpleXmlParsing() throws IOException, URISyntaxException, XMLStreamException,
113 ParserConfigurationException, SAXException {
114 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/foo.xml");
115 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
116 final var result = new NormalizationResultHolder();
117 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
118 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
119 xmlParser.parse(reader);
121 final var transformedInput = result.getResult().data();
122 assertNotNull(transformedInput);
126 void shouldFailOnDuplicateLeaf() throws XMLStreamException, IOException,
127 ParserConfigurationException, SAXException, URISyntaxException {
128 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo.xml");
129 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
130 final var result = new NormalizationResultHolder();
131 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
132 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
133 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
134 assertThat(ex.getMessage(), containsString("""
135 Duplicate element "decimal64-leaf" in namespace "foo-namespace" with parent \
136 "EmptyContainerEffectiveStatement{argument=(foo-namespace)leaf-container}" in XML input"""));
140 void shouldFailOnDuplicateAnyXml() throws XMLStreamException, IOException,
141 ParserConfigurationException, SAXException, URISyntaxException {
142 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-2.xml");
143 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
144 final var result = new NormalizationResultHolder();
145 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
146 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
147 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
148 assertThat(ex.getMessage(), containsString("""
149 Duplicate element "my-anyxml" in namespace "foo-namespace" with parent \
150 "EmptyContainerEffectiveStatement{argument=(foo-namespace)anyxml-container}" in XML input"""));
154 void shouldFailOnDuplicateContainer() throws XMLStreamException, IOException,
155 ParserConfigurationException, SAXException, URISyntaxException {
156 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-3.xml");
157 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
158 final var result = new NormalizationResultHolder();
159 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
160 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
161 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
162 assertThat(ex.getMessage(), containsString("""
163 Duplicate element "leaf-container" in namespace "foo-namespace" with parent \
164 "EmptyContainerEffectiveStatement{argument=(foo-namespace)parent-container}" in XML input"""));
168 void shouldFailOnUnterminatedLeafElement() throws XMLStreamException, IOException,
169 ParserConfigurationException, SAXException, URISyntaxException {
170 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz.xml");
171 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
172 final var result = new NormalizationResultHolder();
173 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
174 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
175 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
176 assertThat(ex.getMessage(), containsString(" START_ELEMENT "));
180 void shouldFailOnUnterminatedLeafElement2() throws XMLStreamException, IOException,
181 ParserConfigurationException, SAXException, URISyntaxException {
182 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-2.xml");
183 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
184 final var result = new NormalizationResultHolder();
185 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
186 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
187 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
188 assertThat(ex.getMessage(), containsString("</my-leaf-1>"));
192 void shouldFailOnUnterminatedContainerElement() throws XMLStreamException, IOException,
193 ParserConfigurationException, SAXException, URISyntaxException {
194 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-4.xml");
195 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
196 final var result = new NormalizationResultHolder();
197 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
198 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
199 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
200 assertThat(ex.getMessage(), containsString("</my-container-1>"));
204 void shouldFailOnUnknownChildNode() throws XMLStreamException, IOException,
205 ParserConfigurationException, SAXException, URISyntaxException {
206 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-3.xml");
207 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
208 final var result = new NormalizationResultHolder();
209 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
210 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
211 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
212 assertThat(ex.getMessage(), containsString("""
213 Schema for node with name my-container-1 and namespace baz-namespace does not exist in parent \
214 EmptyContainerEffectiveStatement{argument=(baz-namespace)my-container-1}"""));
217 private static NormalizedNode buildOuterContainerNode() {
219 MapNode myKeyedListNode = ImmutableNodes.newSystemMapBuilder()
220 .withNodeIdentifier(new NodeIdentifier(MY_KEYED_LIST))
221 .withChild(ImmutableNodes.newMapEntryBuilder()
222 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue1"))
223 .withChild(ImmutableNodes.leafNode(MY_LEAF_IN_LIST_1, "listleafvalue1"))
224 .withChild(ImmutableNodes.leafNode(MY_LEAF_IN_LIST_2, "listleafvalue2"))
226 .withChild(ImmutableNodes.newMapEntryBuilder().withNodeIdentifier(
227 NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue2"))
228 .withChild(ImmutableNodes.leafNode(MY_LEAF_IN_LIST_1, "listleafvalue12"))
229 .withChild(ImmutableNodes.leafNode(MY_LEAF_IN_LIST_2, "listleafvalue22"))
233 LeafNode<?> myLeaf1Node = ImmutableNodes.leafNode(MY_LEAF_1, "value1");
235 SystemLeafSetNode<?> myLeafListNode = ImmutableNodes.newSystemLeafSetBuilder()
236 .withNodeIdentifier(new NodeIdentifier(MY_LEAFLIST))
237 .withChild(ImmutableNodes.leafSetEntry(MY_LEAFLIST, "lflvalue1"))
238 .withChild(ImmutableNodes.leafSetEntry(MY_LEAFLIST, "lflvalue2"))
241 ContainerNode myContainer1Node = ImmutableNodes.newContainerBuilder()
242 .withNodeIdentifier(new NodeIdentifier(MY_CONTAINER_1))
243 .withChild(myKeyedListNode)
244 .withChild(myLeaf1Node)
245 .withChild(myLeafListNode)
249 ContainerNode innerContainerNode = ImmutableNodes.newContainerBuilder()
250 .withNodeIdentifier(new NodeIdentifier(INNER_CONTAINER))
251 .withChild(ImmutableNodes.leafNode(MY_LEAF_2, "value2"))
254 LeafNode<?> myLeaf3Node = ImmutableNodes.leafNode(MY_LEAF_3, "value3");
256 ChoiceNode myChoiceNode = ImmutableNodes.newChoiceBuilder()
257 .withNodeIdentifier(new NodeIdentifier(MY_CHOICE))
258 .withChild(ImmutableNodes.leafNode(MY_LEAF_IN_CASE_2, "case2value"))
261 ContainerNode myContainer2Node = ImmutableNodes.newContainerBuilder()
262 .withNodeIdentifier(new NodeIdentifier(MY_CONTAINER_2))
263 .withChild(innerContainerNode)
264 .withChild(myLeaf3Node)
265 .withChild(myChoiceNode)
269 Map<QName, Object> keys = new HashMap<>();
270 keys.put(MY_FIRST_KEY_LEAF, "listkeyvalue1");
271 keys.put(MY_SECOND_KEY_LEAF, "listkeyvalue2");
273 MapNode myDoublyKeyedListNode = ImmutableNodes.newSystemMapBuilder()
274 .withNodeIdentifier(new NodeIdentifier(MY_DOUBLY_KEYED_LIST))
275 .withChild(ImmutableNodes.newMapEntryBuilder()
276 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_DOUBLY_KEYED_LIST, keys))
277 .withChild(ImmutableNodes.leafNode(MY_LEAF_IN_LIST_3, "listleafvalue1"))
281 ContainerNode myContainer3Node = ImmutableNodes.newContainerBuilder()
282 .withNodeIdentifier(new NodeIdentifier(MY_CONTAINER_3))
283 .withChild(myDoublyKeyedListNode)
286 ContainerNode outerContainerNode = ImmutableNodes.newContainerBuilder()
287 .withNodeIdentifier(new NodeIdentifier(OUTER_CONTAINER))
288 .withChild(myContainer1Node)
289 .withChild(myContainer2Node)
290 .withChild(myContainer3Node)
293 return outerContainerNode;