/*
* Copyright (c) 2016 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
*/
package org.opendaylight.yangtools.yang.data.impl.codec.xml;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.AbstractMap;
import java.util.Date;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
import org.w3c.dom.Document;
public class XmlStreamUtilsTest {
public static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
private static SchemaContext schemaContext;
private static Module leafRefModule;
@BeforeClass
public static void initialize() throws URISyntaxException, FileNotFoundException, ReactorException {
final File file = new File(XmlStreamUtils.class.getResource("/leafref-test.yang").toURI());
schemaContext = YangParserTestUtils.parseYangSources(file);
assertNotNull(schemaContext);
assertEquals(1, schemaContext.getModules().size());
leafRefModule = schemaContext.getModules().iterator().next();
assertNotNull(leafRefModule);
}
@Test
public void testWriteAttribute() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final XMLStreamWriter writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
writer.writeStartElement("element");
QName name = getAttrQName("namespace", "2012-12-12", "attr", Optional.of("prefix"));
final Map.Entry<QName, String> attributeEntry = new AbstractMap.SimpleEntry<>(name, "value");
name = getAttrQName("namespace2", "2012-12-12", "attr", Optional.absent());
final Map.Entry<QName, String> attributeEntryNoPrefix = new AbstractMap.SimpleEntry<>(name, "value");
final RandomPrefix randomPrefix = new RandomPrefix();
XmlStreamUtils.writeAttribute(writer, attributeEntry, randomPrefix);
XmlStreamUtils.writeAttribute(writer, attributeEntryNoPrefix, randomPrefix);
writer.writeEndElement();
writer.close();
out.close();
final String xmlAsString = new String(out.toByteArray());
final Map<String, String> mappedPrefixes = mapPrefixed(randomPrefix.getPrefixes());
assertEquals(2, mappedPrefixes.size());
final String randomPrefixValue = mappedPrefixes.get("namespace2");
final String expectedXmlAsString = "<element xmlns:a=\"namespace\" a:attr=\"value\" xmlns:" + randomPrefixValue
+ "=\"namespace2\" " + randomPrefixValue + ":attr=\"value\"></element>";
XMLUnit.setIgnoreAttributeOrder(true);
final Document control = XMLUnit.buildControlDocument(expectedXmlAsString);
final Document test = XMLUnit.buildTestDocument(xmlAsString);
final Diff diff = XMLUnit.compareXML(control, test);
final boolean identical = diff.identical();
assertTrue("Xml differs: " + diff.toString(), identical);
}
@Test
public void testWriteIdentityRef() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final XMLStreamWriter writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
writer.writeStartElement("element");
final QNameModule parent = QNameModule.create(URI.create("parent:uri"), new Date());
XmlStreamUtils.write(writer, null, QName.create(parent, "identity"), Optional.of(parent));
writer.writeEndElement();
writer.writeStartElement("elementDifferent");
XmlStreamUtils.write(writer, null, QName.create("different:namespace", "identity"), Optional.of(parent));
writer.writeEndElement();
writer.close();
out.close();
final String xmlAsString = new String(out.toByteArray()).replaceAll("\\s*", "");
assertThat(xmlAsString, containsString("element>identity"));
final Pattern prefixedIdentityPattern = Pattern.compile(".*\"different:namespace\">(.*):identity.*");
final Matcher matcher = prefixedIdentityPattern.matcher(xmlAsString);
assertTrue("Xml: " + xmlAsString + " should match: " + prefixedIdentityPattern, matcher.matches());
}
/**
* One leafref reference to other leafref via relative references
*/
@Test
public void testLeafRefRelativeChaining() {
getTargetNodeForLeafRef("leafname3", StringTypeDefinition.class);
}
@Test
public void testLeafRefRelative() {
getTargetNodeForLeafRef("pointToStringLeaf", StringTypeDefinition.class);
}
@Test
public void testLeafRefAbsoluteWithSameTarget() {
getTargetNodeForLeafRef("absname", InstanceIdentifierTypeDefinition.class);
}
/**
* Tests relative path with double point inside path (e. g. "../../lf:interface/../lf:cont2/lf:stringleaf")
*/
@Ignore
// ignored because this isn't implemented
@Test
public void testLeafRefWithDoublePointInPath() {
getTargetNodeForLeafRef("lf-with-double-point-inside", StringTypeDefinition.class);
}
@Test
public void testLeafRefRelativeAndAbsoluteWithSameTarget() {
final TypeDefinition<?> targetNodeForAbsname = getTargetNodeForLeafRef("absname",
InstanceIdentifierTypeDefinition.class);
final TypeDefinition<?> targetNodeForRelname = getTargetNodeForLeafRef("relname",
InstanceIdentifierTypeDefinition.class);
assertEquals(targetNodeForAbsname, targetNodeForRelname);
}
private TypeDefinition<?> getTargetNodeForLeafRef(final String nodeName, final Class<?> clas) {
final LeafSchemaNode schemaNode = findSchemaNodeWithLeafrefType(leafRefModule, nodeName);
assertNotNull(schemaNode);
final LeafrefTypeDefinition leafrefTypedef = findLeafrefType(schemaNode);
assertNotNull(leafrefTypedef);
final TypeDefinition<?> targetBaseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypedef, schemaContext,
schemaNode);
assertTrue("Wrong class found.", clas.isInstance(targetBaseType));
return targetBaseType;
}
private static Map<String, String> mapPrefixed(final Iterable<Map.Entry<URI, String>> prefixes) {
final Map<String, String> mappedPrefixes = Maps.newHashMap();
for (final Map.Entry<URI, String> prefix : prefixes) {
mappedPrefixes.put(prefix.getKey().toString(), prefix.getValue());
}
return mappedPrefixes;
}
private static QName getAttrQName(final String namespace, final String revision, final String localName,
final Optional<String> prefix) {
if (prefix.isPresent()) {
final QName moduleQName = QName.create(namespace, revision, "module");
final QNameModule module = QNameModule.create(moduleQName.getNamespace(), moduleQName.getRevision());
return QName.create(module, localName);
}
return QName.create(namespace, revision, localName);
}
private LeafSchemaNode findSchemaNodeWithLeafrefType(final DataNodeContainer module, final String nodeName) {
for (final DataSchemaNode childNode : module.getChildNodes()) {
if (childNode instanceof DataNodeContainer) {
LeafSchemaNode leafrefFromRecursion = findSchemaNodeWithLeafrefType((DataNodeContainer) childNode,
nodeName);
if (leafrefFromRecursion != null) {
return leafrefFromRecursion;
}
} else if (childNode.getQName().getLocalName().equals(nodeName) && childNode instanceof LeafSchemaNode) {
final TypeDefinition<?> leafSchemaNodeType = ((LeafSchemaNode) childNode).getType();
if (leafSchemaNodeType instanceof LeafrefTypeDefinition) {
return (LeafSchemaNode) childNode;
}
}
}
return null;
}
private static LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) {
final TypeDefinition<?> type = schemaNode.getType();
if (type instanceof LeafrefTypeDefinition) {
return (LeafrefTypeDefinition) type;
}
return null;
}
}