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.common.XMLNamespace;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
32 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
38 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
39 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
40 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
41 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
42 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
43 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
44 import org.xml.sax.SAXException;
46 class XmlToNormalizedNodesTest {
48 private static final QNameModule FOO_MODULE = QNameModule.create(XMLNamespace.of("foo-namespace"));
49 private static final QName PARENT_CONTAINER = QName.create(FOO_MODULE, "parent-container");
51 private static final QNameModule BAZ_MODULE = QNameModule.create(XMLNamespace.of("baz-namespace"));
52 private static final QName OUTER_CONTAINER = QName.create(BAZ_MODULE, "outer-container");
54 private static final QName MY_CONTAINER_1 = QName.create(BAZ_MODULE, "my-container-1");
55 private static final QName MY_KEYED_LIST = QName.create(BAZ_MODULE, "my-keyed-list");
56 private static final QName MY_KEY_LEAF = QName.create(BAZ_MODULE, "my-key-leaf");
57 private static final QName MY_LEAF_IN_LIST_1 = QName.create(BAZ_MODULE, "my-leaf-in-list-1");
58 private static final QName MY_LEAF_IN_LIST_2 = QName.create(BAZ_MODULE, "my-leaf-in-list-2");
59 private static final QName MY_LEAF_1 = QName.create(BAZ_MODULE, "my-leaf-1");
60 private static final QName MY_LEAFLIST = QName.create(BAZ_MODULE, "my-leaf-list");
62 private static final QName MY_CONTAINER_2 = QName.create(BAZ_MODULE, "my-container-2");
63 private static final QName INNER_CONTAINER = QName.create(BAZ_MODULE, "inner-container");
64 private static final QName MY_LEAF_2 = QName.create(BAZ_MODULE, "my-leaf-2");
65 private static final QName MY_LEAF_3 = QName.create(BAZ_MODULE, "my-leaf-3");
66 private static final QName MY_CHOICE = QName.create(BAZ_MODULE, "my-choice");
67 private static final QName MY_LEAF_IN_CASE_2 = QName.create(BAZ_MODULE, "my-leaf-in-case-2");
69 private static final QName MY_CONTAINER_3 = QName.create(BAZ_MODULE, "my-container-3");
70 private static final QName MY_DOUBLY_KEYED_LIST = QName.create(BAZ_MODULE, "my-doubly-keyed-list");
71 private static final QName MY_FIRST_KEY_LEAF = QName.create(BAZ_MODULE, "my-first-key-leaf");
72 private static final QName MY_SECOND_KEY_LEAF = QName.create(BAZ_MODULE, "my-second-key-leaf");
73 private static final QName MY_LEAF_IN_LIST_3 = QName.create(BAZ_MODULE, "my-leaf-in-list-3");
75 private static EffectiveModelContext schemaContext;
76 private static Inference outerContainerSchema;
77 private static Inference parentContainerSchema;
81 schemaContext = YangParserTestUtils.parseYangResourceDirectory("/");
82 parentContainerSchema = Inference.ofDataTreePath(schemaContext, PARENT_CONTAINER);
83 outerContainerSchema = Inference.ofDataTreePath(schemaContext, OUTER_CONTAINER);
87 static void cleanup() {
89 parentContainerSchema = null;
90 outerContainerSchema = null;
94 void testComplexXmlParsing() throws IOException, SAXException, URISyntaxException, XMLStreamException,
95 ParserConfigurationException {
96 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/baz.xml");
97 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
98 final var result = new NormalizationResultHolder();
99 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
101 try (var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema)) {
102 xmlParser.parse(reader);
105 final NormalizedNode transformedInput = result.getResult().data();
106 assertNotNull(transformedInput);
108 final NormalizedNode expectedNormalizedNode = buildOuterContainerNode();
109 assertNotNull(expectedNormalizedNode);
111 assertEquals(expectedNormalizedNode, transformedInput);
115 void testSimpleXmlParsing() throws IOException, URISyntaxException, XMLStreamException,
116 ParserConfigurationException, SAXException {
117 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/foo.xml");
118 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
119 final var result = new NormalizationResultHolder();
120 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
121 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
122 xmlParser.parse(reader);
124 final var transformedInput = result.getResult().data();
125 assertNotNull(transformedInput);
129 void shouldFailOnDuplicateLeaf() throws XMLStreamException, IOException,
130 ParserConfigurationException, SAXException, URISyntaxException {
131 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo.xml");
132 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
133 final var result = new NormalizationResultHolder();
134 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
135 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
136 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
137 assertThat(ex.getMessage(), containsString("""
138 Duplicate element "decimal64-leaf" in namespace "foo-namespace" with parent \
139 "EmptyContainerEffectiveStatement{argument=(foo-namespace)leaf-container}" in XML input"""));
143 void shouldFailOnDuplicateAnyXml() throws XMLStreamException, IOException,
144 ParserConfigurationException, SAXException, URISyntaxException {
145 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-2.xml");
146 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
147 final var result = new NormalizationResultHolder();
148 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
149 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
150 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
151 assertThat(ex.getMessage(), containsString("""
152 Duplicate element "my-anyxml" in namespace "foo-namespace" with parent \
153 "EmptyContainerEffectiveStatement{argument=(foo-namespace)anyxml-container}" in XML input"""));
157 void shouldFailOnDuplicateContainer() throws XMLStreamException, IOException,
158 ParserConfigurationException, SAXException, URISyntaxException {
159 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-3.xml");
160 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
161 final var result = new NormalizationResultHolder();
162 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
163 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
164 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
165 assertThat(ex.getMessage(), containsString("""
166 Duplicate element "leaf-container" in namespace "foo-namespace" with parent \
167 "EmptyContainerEffectiveStatement{argument=(foo-namespace)parent-container}" in XML input"""));
171 void shouldFailOnUnterminatedLeafElement() throws XMLStreamException, IOException,
172 ParserConfigurationException, SAXException, URISyntaxException {
173 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz.xml");
174 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
175 final var result = new NormalizationResultHolder();
176 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
177 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
178 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
179 assertThat(ex.getMessage(), containsString(" START_ELEMENT "));
183 void shouldFailOnUnterminatedLeafElement2() throws XMLStreamException, IOException,
184 ParserConfigurationException, SAXException, URISyntaxException {
185 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-2.xml");
186 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
187 final var result = new NormalizationResultHolder();
188 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
189 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
190 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
191 assertThat(ex.getMessage(), containsString("</my-leaf-1>"));
195 void shouldFailOnUnterminatedContainerElement() throws XMLStreamException, IOException,
196 ParserConfigurationException, SAXException, URISyntaxException {
197 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-4.xml");
198 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
199 final var result = new NormalizationResultHolder();
200 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
201 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
202 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
203 assertThat(ex.getMessage(), containsString("</my-container-1>"));
207 void shouldFailOnUnknownChildNode() throws XMLStreamException, IOException,
208 ParserConfigurationException, SAXException, URISyntaxException {
209 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-3.xml");
210 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
211 final var result = new NormalizationResultHolder();
212 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
213 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
214 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
215 assertThat(ex.getMessage(), containsString("""
216 Schema for node with name my-container-1 and namespace baz-namespace does not exist in parent \
217 EmptyContainerEffectiveStatement{argument=(baz-namespace)my-container-1}"""));
220 private static NormalizedNode buildOuterContainerNode() {
222 MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_KEYED_LIST))
223 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
224 NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue1"))
225 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_1))
226 .withValue("listleafvalue1").build())
227 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_2))
228 .withValue("listleafvalue2").build()).build())
229 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
230 NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue2"))
231 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_1))
232 .withValue("listleafvalue12").build())
233 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_2))
234 .withValue("listleafvalue22").build()).build()).build();
236 LeafNode<?> myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_1))
237 .withValue("value1").build();
239 SystemLeafSetNode<?> myLeafListNode = Builders.leafSetBuilder()
240 .withNodeIdentifier(new NodeIdentifier(MY_LEAFLIST))
241 .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
242 new NodeWithValue<>(MY_LEAFLIST, "lflvalue1")).withValue("lflvalue1").build())
243 .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
244 new NodeWithValue<>(MY_LEAFLIST, "lflvalue2")).withValue("lflvalue2").build()).build();
246 ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier(
247 new NodeIdentifier(MY_CONTAINER_1))
248 .withChild(myKeyedListNode)
249 .withChild(myLeaf1Node)
250 .withChild(myLeafListNode).build();
253 ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier(
254 new NodeIdentifier(INNER_CONTAINER))
255 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_2))
256 .withValue("value2").build()).build();
258 LeafNode<?> myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_3))
259 .withValue("value3").build();
261 ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(MY_CHOICE))
262 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_CASE_2))
263 .withValue("case2value").build()).build();
265 ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier(
266 new NodeIdentifier(MY_CONTAINER_2))
267 .withChild(innerContainerNode)
268 .withChild(myLeaf3Node)
269 .withChild(myChoiceNode).build();
272 Map<QName, Object> keys = new HashMap<>();
273 keys.put(MY_FIRST_KEY_LEAF, "listkeyvalue1");
274 keys.put(MY_SECOND_KEY_LEAF, "listkeyvalue2");
276 MapNode myDoublyKeyedListNode = Builders.mapBuilder()
277 .withNodeIdentifier(new NodeIdentifier(MY_DOUBLY_KEYED_LIST))
278 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
279 NodeIdentifierWithPredicates.of(MY_DOUBLY_KEYED_LIST, keys))
280 .withChild(Builders.leafBuilder().withNodeIdentifier(
281 new NodeIdentifier(MY_LEAF_IN_LIST_3)).withValue("listleafvalue1").build()).build())
284 ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier(
285 new NodeIdentifier(MY_CONTAINER_3))
286 .withChild(myDoublyKeyedListNode).build();
288 ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier(
289 new NodeIdentifier(OUTER_CONTAINER))
290 .withChild(myContainer1Node)
291 .withChild(myContainer2Node)
292 .withChild(myContainer3Node).build();
294 return outerContainerNode;