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.Assert.assertEquals;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertThrows;
16 import com.google.common.collect.ImmutableSet;
17 import java.io.IOException;
18 import java.net.URISyntaxException;
19 import java.util.HashMap;
21 import javax.xml.parsers.ParserConfigurationException;
22 import javax.xml.stream.XMLStreamException;
23 import org.junit.AfterClass;
24 import org.junit.BeforeClass;
25 import org.junit.Test;
26 import org.opendaylight.yangtools.util.xml.UntrustedXML;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.common.QNameModule;
29 import org.opendaylight.yangtools.yang.common.XMLNamespace;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
34 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
41 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
42 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
43 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
44 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
45 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
46 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
47 import org.xml.sax.SAXException;
49 public class XmlToNormalizedNodesTest {
51 private static final QNameModule FOO_MODULE = QNameModule.create(XMLNamespace.of("foo-namespace"));
52 private static final QName PARENT_CONTAINER = QName.create(FOO_MODULE, "parent-container");
54 private static final QNameModule BAZ_MODULE = QNameModule.create(XMLNamespace.of("baz-namespace"));
55 private static final QName OUTER_CONTAINER = QName.create(BAZ_MODULE, "outer-container");
57 private static final QName MY_CONTAINER_1 = QName.create(BAZ_MODULE, "my-container-1");
58 private static final QName MY_KEYED_LIST = QName.create(BAZ_MODULE, "my-keyed-list");
59 private static final QName MY_KEY_LEAF = QName.create(BAZ_MODULE, "my-key-leaf");
60 private static final QName MY_LEAF_IN_LIST_1 = QName.create(BAZ_MODULE, "my-leaf-in-list-1");
61 private static final QName MY_LEAF_IN_LIST_2 = QName.create(BAZ_MODULE, "my-leaf-in-list-2");
62 private static final QName MY_LEAF_1 = QName.create(BAZ_MODULE, "my-leaf-1");
63 private static final QName MY_LEAFLIST = QName.create(BAZ_MODULE, "my-leaf-list");
65 private static final QName MY_CONTAINER_2 = QName.create(BAZ_MODULE, "my-container-2");
66 private static final QName INNER_CONTAINER = QName.create(BAZ_MODULE, "inner-container");
67 private static final QName MY_LEAF_2 = QName.create(BAZ_MODULE, "my-leaf-2");
68 private static final QName MY_LEAF_3 = QName.create(BAZ_MODULE, "my-leaf-3");
69 private static final QName MY_CHOICE = QName.create(BAZ_MODULE, "my-choice");
70 private static final QName MY_LEAF_IN_CASE_2 = QName.create(BAZ_MODULE, "my-leaf-in-case-2");
72 private static final QName MY_CONTAINER_3 = QName.create(BAZ_MODULE, "my-container-3");
73 private static final QName MY_DOUBLY_KEYED_LIST = QName.create(BAZ_MODULE, "my-doubly-keyed-list");
74 private static final QName MY_FIRST_KEY_LEAF = QName.create(BAZ_MODULE, "my-first-key-leaf");
75 private static final QName MY_SECOND_KEY_LEAF = QName.create(BAZ_MODULE, "my-second-key-leaf");
76 private static final QName MY_LEAF_IN_LIST_3 = QName.create(BAZ_MODULE, "my-leaf-in-list-3");
78 private static EffectiveModelContext schemaContext;
79 private static Inference outerContainerSchema;
80 private static Inference parentContainerSchema;
83 public static void setup() {
84 schemaContext = YangParserTestUtils.parseYangResourceDirectory("/");
85 parentContainerSchema = Inference.ofDataTreePath(schemaContext, PARENT_CONTAINER);
86 outerContainerSchema = Inference.ofDataTreePath(schemaContext, OUTER_CONTAINER);
90 public static void cleanup() {
92 parentContainerSchema = null;
93 outerContainerSchema = null;
97 public void testComplexXmlParsing() throws IOException, SAXException, URISyntaxException, XMLStreamException,
98 ParserConfigurationException {
99 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/baz.xml");
100 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
101 final var result = new NormalizationResultHolder();
102 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
104 try (var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema)) {
105 xmlParser.parse(reader);
108 final NormalizedNode transformedInput = result.getResult().data();
109 assertNotNull(transformedInput);
111 final NormalizedNode expectedNormalizedNode = buildOuterContainerNode();
112 assertNotNull(expectedNormalizedNode);
114 assertEquals(expectedNormalizedNode, transformedInput);
118 public void testSimpleXmlParsing() throws IOException, URISyntaxException, XMLStreamException,
119 ParserConfigurationException, SAXException {
120 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/foo.xml");
121 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
122 final var result = new NormalizationResultHolder();
123 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
124 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
125 xmlParser.parse(reader);
127 final var transformedInput = result.getResult().data();
128 assertNotNull(transformedInput);
132 public void shouldFailOnDuplicateLeaf() throws XMLStreamException, IOException,
133 ParserConfigurationException, SAXException, URISyntaxException {
134 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo.xml");
135 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
136 final var result = new NormalizationResultHolder();
137 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
138 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
139 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
140 assertThat(ex.getMessage(), containsString("""
141 Duplicate element "decimal64-leaf" in namespace "foo-namespace" with parent \
142 "EmptyContainerEffectiveStatement{argument=(foo-namespace)leaf-container}" in XML input"""));
146 public void shouldFailOnDuplicateAnyXml() throws XMLStreamException, IOException,
147 ParserConfigurationException, SAXException, URISyntaxException {
148 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-2.xml");
149 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
150 final var result = new NormalizationResultHolder();
151 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
152 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
153 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
154 assertThat(ex.getMessage(), containsString("""
155 Duplicate element "my-anyxml" in namespace "foo-namespace" with parent \
156 "EmptyContainerEffectiveStatement{argument=(foo-namespace)anyxml-container}" in XML input"""));
160 public void shouldFailOnDuplicateContainer() throws XMLStreamException, IOException,
161 ParserConfigurationException, SAXException, URISyntaxException {
162 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-3.xml");
163 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
164 final var result = new NormalizationResultHolder();
165 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
166 final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
167 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
168 assertThat(ex.getMessage(), containsString("""
169 Duplicate element "leaf-container" in namespace "foo-namespace" with parent \
170 "EmptyContainerEffectiveStatement{argument=(foo-namespace)parent-container}" in XML input"""));
174 public void shouldFailOnUnterminatedLeafElement() throws XMLStreamException, IOException,
175 ParserConfigurationException, SAXException, URISyntaxException {
176 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz.xml");
177 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
178 final var result = new NormalizationResultHolder();
179 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
180 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
181 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
182 assertThat(ex.getMessage(), containsString(" START_ELEMENT "));
186 public void shouldFailOnUnterminatedLeafElement2() throws XMLStreamException, IOException,
187 ParserConfigurationException, SAXException, URISyntaxException {
188 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-2.xml");
189 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
190 final var result = new NormalizationResultHolder();
191 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
192 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
193 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
194 assertThat(ex.getMessage(), containsString("</my-leaf-1>"));
198 public void shouldFailOnUnterminatedContainerElement() throws XMLStreamException, IOException,
199 ParserConfigurationException, SAXException, URISyntaxException {
200 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-4.xml");
201 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
202 final var result = new NormalizationResultHolder();
203 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
204 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
205 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
206 assertThat(ex.getMessage(), containsString("</my-container-1>"));
210 public void shouldFailOnUnknownChildNode() throws XMLStreamException, IOException,
211 ParserConfigurationException, SAXException, URISyntaxException {
212 final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-3.xml");
213 final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
214 final var result = new NormalizationResultHolder();
215 final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
216 final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
217 final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
218 assertThat(ex.getMessage(), containsString("""
219 Schema for node with name my-container-1 and namespace baz-namespace does not exist in parent \
220 EmptyContainerEffectiveStatement{argument=(baz-namespace)my-container-1}"""));
223 private static NormalizedNode buildOuterContainerNode() {
225 MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_KEYED_LIST))
226 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
227 NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue1"))
228 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_1))
229 .withValue("listleafvalue1").build())
230 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_2))
231 .withValue("listleafvalue2").build()).build())
232 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
233 NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue2"))
234 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_1))
235 .withValue("listleafvalue12").build())
236 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_2))
237 .withValue("listleafvalue22").build()).build()).build();
239 LeafNode<?> myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_1))
240 .withValue("value1").build();
242 SystemLeafSetNode<?> myLeafListNode = Builders.leafSetBuilder()
243 .withNodeIdentifier(new NodeIdentifier(MY_LEAFLIST))
244 .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
245 new NodeWithValue<>(MY_LEAFLIST, "lflvalue1")).withValue("lflvalue1").build())
246 .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
247 new NodeWithValue<>(MY_LEAFLIST, "lflvalue2")).withValue("lflvalue2").build()).build();
249 ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier(
250 new NodeIdentifier(MY_CONTAINER_1))
251 .withChild(myKeyedListNode)
252 .withChild(myLeaf1Node)
253 .withChild(myLeafListNode).build();
256 ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier(
257 new NodeIdentifier(INNER_CONTAINER))
258 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_2))
259 .withValue("value2").build()).build();
261 LeafNode<?> myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_3))
262 .withValue("value3").build();
264 ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(MY_CHOICE))
265 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_CASE_2))
266 .withValue("case2value").build()).build();
268 ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier(
269 new NodeIdentifier(MY_CONTAINER_2))
270 .withChild(innerContainerNode)
271 .withChild(myLeaf3Node)
272 .withChild(myChoiceNode).build();
275 Map<QName, Object> keys = new HashMap<>();
276 keys.put(MY_FIRST_KEY_LEAF, "listkeyvalue1");
277 keys.put(MY_SECOND_KEY_LEAF, "listkeyvalue2");
279 MapNode myDoublyKeyedListNode = Builders.mapBuilder()
280 .withNodeIdentifier(new NodeIdentifier(MY_DOUBLY_KEYED_LIST))
281 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
282 NodeIdentifierWithPredicates.of(MY_DOUBLY_KEYED_LIST, keys))
283 .withChild(Builders.leafBuilder().withNodeIdentifier(
284 new NodeIdentifier(MY_LEAF_IN_LIST_3)).withValue("listleafvalue1").build()).build())
287 AugmentationNode myDoublyKeyedListAugNode = Builders.augmentationBuilder().withNodeIdentifier(
288 new AugmentationIdentifier(ImmutableSet.of(MY_DOUBLY_KEYED_LIST)))
289 .withChild(myDoublyKeyedListNode).build();
291 ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier(
292 new NodeIdentifier(MY_CONTAINER_3))
293 .withChild(myDoublyKeyedListAugNode).build();
295 AugmentationNode myContainer3AugNode = Builders.augmentationBuilder().withNodeIdentifier(
296 new AugmentationIdentifier(ImmutableSet.of(MY_CONTAINER_3)))
297 .withChild(myContainer3Node).build();
299 ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier(
300 new NodeIdentifier(OUTER_CONTAINER))
301 .withChild(myContainer1Node)
302 .withChild(myContainer2Node)
303 .withChild(myContainer3AugNode).build();
305 return outerContainerNode;