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
9 package org.opendaylight.yangtools.yang.data.codec.xml;
11 import static org.hamcrest.CoreMatchers.containsString;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertThat;
15 import static org.junit.Assert.assertTrue;
17 import java.io.ByteArrayOutputStream;
18 import java.io.IOException;
20 import java.util.AbstractMap;
21 import java.util.HashMap;
23 import java.util.Optional;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26 import javax.xml.stream.XMLOutputFactory;
27 import javax.xml.stream.XMLStreamException;
28 import javax.xml.stream.XMLStreamWriter;
29 import org.custommonkey.xmlunit.Diff;
30 import org.custommonkey.xmlunit.XMLUnit;
31 import org.junit.AfterClass;
32 import org.junit.BeforeClass;
33 import org.junit.Ignore;
34 import org.junit.Test;
35 import org.opendaylight.yangtools.yang.common.QName;
36 import org.opendaylight.yangtools.yang.common.QNameModule;
37 import org.opendaylight.yangtools.yang.common.Revision;
38 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
39 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.Module;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
46 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
47 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
48 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
49 import org.w3c.dom.Document;
51 public class XmlStreamUtilsTest {
53 interface XMLStreamWriterConsumer {
54 void accept(XMLStreamWriter writer) throws XMLStreamException;
57 public static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
59 private static SchemaContext schemaContext;
60 private static Module leafRefModule;
63 public static void initialize() {
64 schemaContext = YangParserTestUtils.parseYangResource("/leafref-test.yang");
65 assertNotNull(schemaContext);
66 assertEquals(1, schemaContext.getModules().size());
67 leafRefModule = schemaContext.getModules().iterator().next();
68 assertNotNull(leafRefModule);
72 public static void cleanup() {
78 public void testWriteAttribute() throws Exception {
79 final ByteArrayOutputStream out = new ByteArrayOutputStream();
80 final XMLStreamWriter writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
81 writer.writeStartElement("element");
83 QName name = getAttrQName("namespace", "2012-12-12", "attr", Optional.of("prefix"));
84 final Map.Entry<QName, String> attributeEntry = new AbstractMap.SimpleEntry<>(name, "value");
86 name = getAttrQName("namespace2", "2012-12-12", "attr", Optional.empty());
87 final Map.Entry<QName, String> attributeEntryNoPrefix = new AbstractMap.SimpleEntry<>(name, "value");
89 final RandomPrefix randomPrefix = new RandomPrefix(null);
90 XMLStreamWriterUtils.writeAttribute(writer, attributeEntry, randomPrefix);
91 XMLStreamWriterUtils.writeAttribute(writer, attributeEntryNoPrefix, randomPrefix);
93 writer.writeEndElement();
97 final String xmlAsString = new String(out.toByteArray());
99 final Map<String, String> mappedPrefixes = mapPrefixed(randomPrefix.getPrefixes());
100 assertEquals(2, mappedPrefixes.size());
101 final String randomPrefixValue = mappedPrefixes.get("namespace2");
103 final String expectedXmlAsString = "<element xmlns:a=\"namespace\" a:attr=\"value\" xmlns:" + randomPrefixValue
104 + "=\"namespace2\" " + randomPrefixValue + ":attr=\"value\"></element>";
106 XMLUnit.setIgnoreAttributeOrder(true);
107 final Document control = XMLUnit.buildControlDocument(expectedXmlAsString);
108 final Document test = XMLUnit.buildTestDocument(xmlAsString);
109 final Diff diff = XMLUnit.compareXML(control, test);
111 final boolean identical = diff.identical();
112 assertTrue("Xml differs: " + diff.toString(), identical);
116 public void testWriteIdentityRef() throws Exception {
117 final QNameModule parent = QNameModule.create(URI.create("parent:uri"), Revision.of("2000-01-01"));
119 String xmlAsString = createXml(writer -> {
120 writer.writeStartElement("element");
121 XMLStreamWriterUtils.write(writer, null, QName.create(parent, "identity"), parent);
122 writer.writeEndElement();
125 assertThat(xmlAsString, containsString("element>identity"));
127 xmlAsString = createXml(writer -> {
128 writer.writeStartElement("elementDifferent");
129 XMLStreamWriterUtils.write(writer, null, QName.create("different:namespace", "identity"), parent);
130 writer.writeEndElement();
134 final Pattern prefixedIdentityPattern = Pattern.compile(".*\"different:namespace\">(.*):identity.*");
135 final Matcher matcher = prefixedIdentityPattern.matcher(xmlAsString);
136 assertTrue("Xml: " + xmlAsString + " should match: " + prefixedIdentityPattern, matcher.matches());
139 private static String createXml(XMLStreamWriterConsumer consumer) throws XMLStreamException, IOException {
140 final ByteArrayOutputStream out = new ByteArrayOutputStream();
141 final XMLStreamWriter writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
143 consumer.accept(writer);
148 return new String(out.toByteArray()).replaceAll("\\s*", "");
152 * One leafref reference to other leafref via relative references.
155 public void testLeafRefRelativeChaining() {
156 getTargetNodeForLeafRef("leafname3", StringTypeDefinition.class);
160 public void testLeafRefRelative() {
161 getTargetNodeForLeafRef("pointToStringLeaf", StringTypeDefinition.class);
165 public void testLeafRefAbsoluteWithSameTarget() {
166 getTargetNodeForLeafRef("absname", InstanceIdentifierTypeDefinition.class);
170 * Tests relative path with double point inside path (e. g. "../../lf:interface/../lf:cont2/lf:stringleaf")
172 // ignored because this isn't implemented
175 public void testLeafRefWithDoublePointInPath() {
176 getTargetNodeForLeafRef("lf-with-double-point-inside", StringTypeDefinition.class);
180 public void testLeafRefRelativeAndAbsoluteWithSameTarget() {
181 final TypeDefinition<?> targetNodeForAbsname = getTargetNodeForLeafRef("absname",
182 InstanceIdentifierTypeDefinition.class);
183 final TypeDefinition<?> targetNodeForRelname = getTargetNodeForLeafRef("relname",
184 InstanceIdentifierTypeDefinition.class);
185 assertEquals(targetNodeForAbsname, targetNodeForRelname);
188 private TypeDefinition<?> getTargetNodeForLeafRef(final String nodeName, final Class<?> clas) {
189 final LeafSchemaNode schemaNode = findSchemaNodeWithLeafrefType(leafRefModule, nodeName);
190 assertNotNull(schemaNode);
191 final LeafrefTypeDefinition leafrefTypedef = findLeafrefType(schemaNode);
192 assertNotNull(leafrefTypedef);
193 final TypeDefinition<?> targetBaseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypedef, schemaContext,
195 assertTrue("Wrong class found.", clas.isInstance(targetBaseType));
196 return targetBaseType;
199 private static Map<String, String> mapPrefixed(final Iterable<Map.Entry<URI, String>> prefixes) {
200 final Map<String, String> mappedPrefixes = new HashMap<>();
201 for (final Map.Entry<URI, String> prefix : prefixes) {
202 mappedPrefixes.put(prefix.getKey().toString(), prefix.getValue());
204 return mappedPrefixes;
207 private static QName getAttrQName(final String namespace, final String revision, final String localName,
208 final Optional<String> prefix) {
209 if (prefix.isPresent()) {
210 final QName moduleQName = QName.create(namespace, revision, "module");
211 final QNameModule module = QNameModule.create(moduleQName.getNamespace(), moduleQName.getRevision());
212 return QName.create(module, localName);
214 return QName.create(namespace, revision, localName);
217 private LeafSchemaNode findSchemaNodeWithLeafrefType(final DataNodeContainer module, final String nodeName) {
218 for (final DataSchemaNode childNode : module.getChildNodes()) {
219 if (childNode instanceof DataNodeContainer) {
220 LeafSchemaNode leafrefFromRecursion = findSchemaNodeWithLeafrefType((DataNodeContainer) childNode,
222 if (leafrefFromRecursion != null) {
223 return leafrefFromRecursion;
225 } else if (childNode.getQName().getLocalName().equals(nodeName) && childNode instanceof LeafSchemaNode) {
226 final TypeDefinition<?> leafSchemaNodeType = ((LeafSchemaNode) childNode).getType();
227 if (leafSchemaNodeType instanceof LeafrefTypeDefinition) {
228 return (LeafSchemaNode) childNode;
235 private static LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) {
236 final TypeDefinition<?> type = schemaNode.getType();
237 if (type instanceof LeafrefTypeDefinition) {
238 return (LeafrefTypeDefinition) type;