<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-util</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-codec-xml</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-parser-impl</artifactId>
package org.opendaylight.yangtools.yang.data.codec.gson;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadModules;
import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
-import com.google.common.base.Preconditions;
+
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URISyntaxException;
-import java.util.Collections;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
-import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class YangModeledAnyXmlSupportTest {
- private static final XMLOutputFactory XML_FACTORY;
- private static final DocumentBuilderFactory BUILDERFACTORY;
-
- static {
- XML_FACTORY = XMLOutputFactory.newFactory();
- XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
-
- BUILDERFACTORY = DocumentBuilderFactory.newInstance();
- BUILDERFACTORY.setNamespaceAware(true);
- BUILDERFACTORY.setCoalescing(true);
- BUILDERFACTORY.setIgnoringElementContentWhitespace(true);
- BUILDERFACTORY.setIgnoringComments(true);
- }
-
private static SchemaContext schemaContext;
- private static Document xmlDoc;
private static ContainerNode data;
@BeforeClass
- public static void init() throws IOException, URISyntaxException, ReactorException, SAXException {
+ public static void init() throws IOException, URISyntaxException, ReactorException, SAXException,
+ XMLStreamException, ParserConfigurationException {
schemaContext = loadModules("/yang-modeled-anyxml/yang");
- xmlDoc = loadDocument("/yang-modeled-anyxml/xml/baz.xml");
- data = DomToNormalizedNodeParserFactory
- .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext).getContainerNodeParser()
- .parse(Collections.singletonList(xmlDoc.getDocumentElement()), schemaContext);
+
+ final InputStream resourceAsStream = YangModeledAnyXmlSupportTest.class.getResourceAsStream(
+ "/yang-modeled-anyxml/xml/baz.xml");
+
+ final XMLInputFactory factory = XMLInputFactory.newInstance();
+ final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream);
+
+ final NormalizedNodeResult result = new NormalizedNodeResult();
+
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+ final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext);
+ xmlParser.parse(reader);
+
+ assertNotNull(result.getResult());
+ assertTrue(result.getResult() instanceof ContainerNode);
+ data = (ContainerNode) result.getResult();
}
@Test
jsonParser.parse(new JsonReader(new StringReader(inputJson)));
final NormalizedNode<?, ?> transformedInput = result.getResult();
- assertEquals(data.getValue().iterator().next(), transformedInput);
+ assertEquals(data, transformedInput);
}
@Test
public void normalizedNodesToJsonTest() throws IOException, URISyntaxException, SAXException {
- final DataContainerChild<? extends PathArgument, ?> baz = data.getValue().iterator().next();
+ final DataContainerChild<? extends PathArgument, ?> baz = data;
final Writer writer = new StringWriter();
final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, baz);
nodeWriter.close();
return writer.toString();
}
-
- private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
- final InputStream resourceAsStream = YangModeledAnyXmlSupportTest.class.getResourceAsStream(xmlPath);
-
- final Document currentConfigElement = readXmlToDocument(resourceAsStream);
- Preconditions.checkNotNull(currentConfigElement);
- return currentConfigElement;
- }
-
- private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
- final DocumentBuilder dBuilder;
- try {
- dBuilder = BUILDERFACTORY.newDocumentBuilder();
- } catch (final ParserConfigurationException e) {
- throw new RuntimeException("Failed to parse XML document", e);
- }
- final Document doc = dBuilder.parse(xmlContent);
-
- doc.getDocumentElement().normalize();
- return doc;
- }
-
}
\ No newline at end of file
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>xmlunit</groupId>
+ <artifactId>xmlunit</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<!--
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.xml;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import java.net.URI;
+import java.util.Map.Entry;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+
+class RandomPrefix {
+ // 32 characters, carefully chosen
+ private static final String LOOKUP = "abcdefghiknoprstABCDEFGHIKNOPRST";
+ private static final int MASK = 0x1f;
+ private static final int SHIFT = 5;
+
+ private int counter = 0;
+
+ // BiMap to make values lookup faster
+ private final BiMap<URI, String> prefixes = HashBiMap.create();
+ private final NamespaceContext context;
+
+ RandomPrefix() {
+ this.context = null;
+ }
+
+ RandomPrefix(final NamespaceContext context) {
+ this.context = Preconditions.checkNotNull(context);
+ }
+
+ Iterable<Entry<URI, String>> getPrefixes() {
+ return prefixes.entrySet();
+ }
+
+ String encodePrefix(final URI namespace) {
+ String prefix = prefixes.get(namespace);
+ if (prefix != null) {
+ return prefix;
+ }
+
+ do {
+ prefix = encode(counter);
+ counter++;
+ } while (alreadyUsedPrefix(prefix));
+
+ prefixes.put(namespace, prefix);
+ return prefix;
+ }
+
+ private boolean alreadyUsedPrefix(final String prefix) {
+ if (context == null) {
+ return false;
+ }
+
+ final String str = context.getNamespaceURI(prefix);
+ return !XMLConstants.NULL_NS_URI.equals(str);
+ }
+
+ @VisibleForTesting
+ static int decode(final String str) {
+ int ret = 0;
+ for (char c : str.toCharArray()) {
+ int idx = LOOKUP.indexOf(c);
+ Preconditions.checkArgument(idx != -1, "Invalid string %s", str);
+ ret = (ret << SHIFT) + idx;
+ }
+
+ return ret;
+ }
+
+ @VisibleForTesting
+ static String encode(int num) {
+ final StringBuilder sb = new StringBuilder();
+
+ do {
+ sb.append(LOOKUP.charAt(num & MASK));
+ num >>>= SHIFT;
+ } while (num != 0);
+
+ return sb.reverse().toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.xml;
+
+import java.net.URI;
+import java.util.Map.Entry;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.util.AbstractStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class RandomPrefixInstanceIdentifierSerializer extends AbstractStringInstanceIdentifierCodec {
+ private final RandomPrefix prefixes = new RandomPrefix();
+ private final DataSchemaContextTree schemaTree;
+
+
+ RandomPrefixInstanceIdentifierSerializer(SchemaContext ctx) {
+ schemaTree = DataSchemaContextTree.from(ctx);
+ }
+
+ Iterable<Entry<URI, String>> getPrefixes() {
+ return prefixes.getPrefixes();
+ }
+
+ @Override
+ protected String prefixForNamespace(final URI namespace) {
+ return prefixes.encodePrefix(namespace);
+ }
+
+ @Override
+ protected QName createQName(final String prefix, final String localName) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ protected DataSchemaContextTree getDataContextTree() {
+ return schemaTree;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2016 Brocade Communications 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.codec.xml;
+
+import com.google.common.base.Strings;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.annotation.Nonnull;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+final class SchemaAwareXMLStreamNormalizedNodeStreamWriter extends XMLStreamNormalizedNodeStreamWriter<SchemaNode> {
+ private final SchemaTracker tracker;
+ private final XmlStreamUtils streamUtils;
+
+ private SchemaAwareXMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final SchemaContext context,
+ final SchemaPath path) {
+ super(writer);
+ this.tracker = SchemaTracker.create(context, path);
+ this.streamUtils = XmlStreamUtils.create(context);
+ }
+
+ static NormalizedNodeStreamWriter newInstance(final XMLStreamWriter writer, final SchemaContext context,
+ final SchemaPath path) {
+ return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context, path);
+ }
+
+ @Override
+ protected void writeAttributes(@Nonnull final Map<QName, String> attributes) throws IOException {
+ for (final Entry<QName, String> qNameStringEntry : attributes.entrySet()) {
+ try {
+ final String namespace = qNameStringEntry.getKey().getNamespace().toString();
+
+ if (Strings.isNullOrEmpty(namespace)) {
+ writer.writeAttribute(qNameStringEntry.getKey().getLocalName(), qNameStringEntry.getValue());
+ } else {
+ writer.writeAttribute(namespace, qNameStringEntry.getKey().getLocalName(), qNameStringEntry.getValue());
+ }
+ } catch (final XMLStreamException e) {
+ throw new IOException("Unable to emit attribute " + qNameStringEntry, e);
+ }
+ }
+ }
+
+ @Override
+ protected void writeValue(final XMLStreamWriter xmlWriter, final QName qname, final Object value,
+ final SchemaNode schemaNode) throws IOException, XMLStreamException {
+ streamUtils.writeValue(xmlWriter, schemaNode, value, qname.getModule());
+ }
+
+ @Override
+ protected void startList(final NodeIdentifier name) {
+ tracker.startList(name);
+ }
+
+ @Override
+ protected void startListItem(final PathArgument name) throws IOException {
+ tracker.startListItem(name);
+ startElement(name.getNodeType());
+ }
+
+ @Override
+ protected void endNode(final XMLStreamWriter xmlWriter) throws IOException, XMLStreamException {
+ final Object schema = tracker.endNode();
+ if (schema instanceof ListSchemaNode) {
+ // For lists, we only emit end element on the inner frame
+ final Object parent = tracker.getParent();
+ if (parent == schema) {
+ xmlWriter.writeEndElement();
+ }
+ } else if (schema instanceof ContainerSchemaNode) {
+ // Emit container end element
+ xmlWriter.writeEndElement();
+ }
+ }
+
+ @Override
+ public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
+ final LeafSchemaNode schema = tracker.leafNode(name);
+ writeElement(schema.getQName(), value, Collections.emptyMap(), schema);
+ }
+
+ @Override
+ public void leafNode(NodeIdentifier name, Object value, Map<QName, String> attributes) throws IOException {
+ final LeafSchemaNode schema = tracker.leafNode(name);
+ writeElement(schema.getQName(), value, attributes, schema);
+ }
+
+ @Override
+ public void leafSetEntryNode(final QName name, final Object value, final Map<QName, String> attributes)
+ throws IOException {
+ final LeafListSchemaNode schema = tracker.leafSetEntryNode();
+ writeElement(schema.getQName(), value, attributes, schema);
+ }
+
+ @Override
+ public void leafSetEntryNode(final QName name, final Object value) throws IOException {
+ final LeafListSchemaNode schema = tracker.leafSetEntryNode();
+ writeElement(schema.getQName(), value, Collections.emptyMap(), schema);
+ }
+ @Override
+ public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
+ tracker.startLeafSet(name);
+ }
+
+ @Override
+ public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
+ tracker.startLeafSet(name);
+ }
+
+ @Override
+ public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ final SchemaNode schema = tracker.startContainerNode(name);
+ startElement(schema.getQName());
+ }
+
+ @Override
+ public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
+ tracker.startChoiceNode(name);
+ }
+
+ @Override
+ public void startAugmentationNode(final AugmentationIdentifier identifier) {
+ tracker.startAugmentationNode(identifier);
+ }
+
+ @Override
+ public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ final SchemaNode schema = tracker.startYangModeledAnyXmlNode(name);
+ startElement(schema.getQName());
+ }
+
+ @Override
+ public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
+ final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
+ anyxmlNode(schema.getQName(), value);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Brocade Communications 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.codec.xml;
+
+import com.google.common.base.Strings;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.annotation.Nonnull;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+class SchemalessXMLStreamNormalizedNodeStreamWriter extends XMLStreamNormalizedNodeStreamWriter<Object> {
+ private enum ContainerType {
+ CONTAINER,
+ LEAF_SET,
+ LIST,
+ LIST_ITEM,
+ ANY_XML,
+ CHOICE,
+ AUGMENTATION
+ }
+
+ private final Deque<ContainerType> containerTypeStack = new ArrayDeque<>();
+ private final RandomPrefix randomPrefix;
+
+ private SchemalessXMLStreamNormalizedNodeStreamWriter(XMLStreamWriter writer) {
+ super(writer);
+ randomPrefix = new RandomPrefix();
+ }
+
+ static NormalizedNodeStreamWriter newInstance(XMLStreamWriter writer) {
+ return new SchemalessXMLStreamNormalizedNodeStreamWriter(writer);
+ }
+
+ @Override
+ public void leafNode(NodeIdentifier name, Object value, Map<QName, String> attributes) throws IOException {
+ writeElement(name.getNodeType(), value, attributes, null);
+ }
+
+ @Override
+ public void leafSetEntryNode(QName name, Object value, Map<QName, String> attributes) throws IOException {
+ writeElement(name, value, attributes, null);
+ }
+
+ @Override
+ public void leafNode(NodeIdentifier name, Object value) throws IOException {
+ writeElement(name.getNodeType(), value, Collections.emptyMap(), null);
+ }
+
+ @Override
+ public void leafSetEntryNode(QName name, Object value) throws IOException {
+ writeElement(name, value, Collections.emptyMap(), null);
+ }
+
+ @Override
+ public void startLeafSet(NodeIdentifier name, int childSizeHint) throws IOException {
+ containerTypeStack.push(ContainerType.LEAF_SET);
+ }
+
+ @Override
+ public void startOrderedLeafSet(NodeIdentifier name, int childSizeHint)
+ throws IOException, IllegalArgumentException {
+ containerTypeStack.push(ContainerType.LEAF_SET);
+ }
+
+ @Override
+ public void startContainerNode(NodeIdentifier name, int childSizeHint) throws IOException {
+ containerTypeStack.push(ContainerType.CONTAINER);
+ startElement(name.getNodeType());
+ }
+
+ @Override
+ public void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IOException {
+ containerTypeStack.push(ContainerType.CHOICE);
+ }
+
+ @Override
+ public void startAugmentationNode(AugmentationIdentifier identifier) throws IOException {
+ containerTypeStack.push(ContainerType.AUGMENTATION);
+ }
+
+ @Override
+ public void anyxmlNode(NodeIdentifier name, Object value) throws IOException {
+ anyxmlNode(name.getNodeType(), value);
+ }
+
+ @Override
+ public void startYangModeledAnyXmlNode(NodeIdentifier name, int childSizeHint) throws IOException {
+ containerTypeStack.push(ContainerType.ANY_XML);
+ startElement(name.getNodeType());
+ }
+
+ @Override
+ protected void writeAttributes(@Nonnull final Map<QName, String> attributes) throws IOException {
+ for (final Entry<QName, String> qNameStringEntry : attributes.entrySet()) {
+ try {
+ final String namespace = qNameStringEntry.getKey().getNamespace().toString();
+
+ if (Strings.isNullOrEmpty(namespace)) {
+ writer.writeAttribute(qNameStringEntry.getKey().getLocalName(), qNameStringEntry.getValue());
+ } else {
+ final String prefix = randomPrefix.encodePrefix(qNameStringEntry.getKey().getNamespace());
+ writer.writeAttribute(prefix, namespace, qNameStringEntry.getKey().getLocalName(), qNameStringEntry
+ .getValue());
+ }
+ } catch (final XMLStreamException e) {
+ throw new IOException("Unable to emit attribute " + qNameStringEntry, e);
+ }
+ }
+ }
+
+ @Override
+ protected void writeValue(XMLStreamWriter xmlWriter, QName qname, Object value, Object context)
+ throws XMLStreamException {
+ xmlWriter.writeCharacters(value.toString());
+ }
+
+ @Override
+ protected void startList(NodeIdentifier name) {
+ containerTypeStack.push(ContainerType.LIST);
+ }
+
+ @Override
+ protected void startListItem(PathArgument name) throws IOException {
+ containerTypeStack.push(ContainerType.LIST_ITEM);
+ startElement(name.getNodeType());
+ }
+
+ @Override
+ protected void endNode(XMLStreamWriter xmlWriter) throws IOException, XMLStreamException {
+ ContainerType type = containerTypeStack.pop();
+ switch (type) {
+ case CONTAINER:
+ case LIST_ITEM:
+ case ANY_XML:
+ xmlWriter.writeEndElement();
+ break;
+ default:
+ break;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.xml;
+
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stax.StAXResult;
+import javax.xml.transform.stream.StreamResult;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+/**
+ * A {@link NormalizedNodeStreamWriter} which translates the events into an {@link XMLStreamWriter},
+ * resulting in a RFC 6020 XML encoding. There are 2 versions of this class, one that takes a
+ * SchemaContext and encodes values appropriately according to the yang schema. The other is
+ * schema-less and merely outputs values using toString. The latter is intended for debugging
+ * where doesn't have a SchemaContext available and isn't meant for production use.
+ */
+public abstract class XMLStreamNormalizedNodeStreamWriter<T> implements NormalizedNodeStreamAttributeWriter {
+ private static final Logger LOG = LoggerFactory.getLogger(XMLStreamNormalizedNodeStreamWriter.class);
+ private static final String COM_SUN_TRANSFORMER = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";
+
+ private static final TransformerFactory TRANSFORMER_FACTORY;
+ static {
+ TransformerFactory f = TransformerFactory.newInstance();
+ if (!f.getFeature(StAXResult.FEATURE)) {
+ LOG.warn("Platform-default TransformerFactory {} does not support StAXResult, attempting fallback to {}",
+ f, COM_SUN_TRANSFORMER);
+ f = TransformerFactory.newInstance(COM_SUN_TRANSFORMER, null);
+ if (!f.getFeature(StAXResult.FEATURE)) {
+ throw new TransformerFactoryConfigurationError("No TransformerFactory supporting StAXResult found.");
+ }
+ }
+
+ TRANSFORMER_FACTORY = f;
+ }
+
+ final XMLStreamWriter writer;
+
+ XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer) {
+ this.writer = Preconditions.checkNotNull(writer);
+ }
+
+ /**
+ * Create a new writer with the specified context as its root.
+ *
+ * @param writer Output {@link XMLStreamWriter}
+ * @param context Associated {@link SchemaContext}.
+ * @return A new {@link NormalizedNodeStreamWriter}
+ */
+ public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context) {
+ return create( writer, context, SchemaPath.ROOT);
+ }
+
+ /**
+ * Create a new writer with the specified context and rooted in the specified schema path
+ *
+ * @param writer Output {@link XMLStreamWriter}
+ * @param context Associated {@link SchemaContext}.
+ * @param path path
+ *
+ * @return A new {@link NormalizedNodeStreamWriter}
+ */
+ public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context,
+ final SchemaPath path) {
+ return SchemaAwareXMLStreamNormalizedNodeStreamWriter.newInstance(writer, context, path);
+ }
+
+ /**
+ * Create a new schema-less writer. Note that this version is intended for debugging
+ * where doesn't have a SchemaContext available and isn't meant for production use.
+ *
+ * @param writer Output {@link XMLStreamWriter}
+ *
+ * @return A new {@link NormalizedNodeStreamWriter}
+ */
+ public static NormalizedNodeStreamWriter createSchemaless(final XMLStreamWriter writer) {
+ return SchemalessXMLStreamNormalizedNodeStreamWriter.newInstance(writer);
+ }
+
+ abstract void writeAttributes(@Nonnull final Map<QName, String> attributes) throws IOException;
+
+ abstract void writeValue(final XMLStreamWriter xmlWriter, final QName qname,
+ @Nonnull final Object value, T context) throws IOException, XMLStreamException;
+
+ abstract void startList(final NodeIdentifier name);
+
+ abstract void startListItem(final PathArgument name) throws IOException;
+
+ abstract void endNode(XMLStreamWriter xmlWriter) throws IOException, XMLStreamException;
+
+ private void writeStartElement(final QName qname) throws XMLStreamException {
+ String ns = qname.getNamespace().toString();
+ writer.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, qname.getLocalName(), ns);
+ if (writer.getNamespaceContext() != null) {
+ String parentNs = writer.getNamespaceContext().getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX);
+ if (!ns.equals(parentNs)) {
+ writer.writeDefaultNamespace(ns);
+ }
+ }
+ }
+
+ void writeElement(final QName qname, final Object value, @Nullable final Map<QName, String> attributes,
+ final T context) throws IOException {
+ try {
+ writeStartElement(qname);
+
+ writeAttributes(attributes);
+ if (value != null) {
+ writeValue(writer, qname, value, context);
+ }
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new IOException("Failed to emit element", e);
+ }
+ }
+
+ void startElement(final QName qname) throws IOException {
+ try {
+ writeStartElement(qname);
+ } catch (XMLStreamException e) {
+ throw new IOException("Failed to start element", e);
+ }
+ }
+
+ void anyxmlNode(final QName qname, final Object value) throws IOException {
+ if (value != null) {
+ Preconditions.checkArgument(value instanceof DOMSource, "AnyXML value must be DOMSource, not %s", value);
+ final DOMSource domSource = (DOMSource) value;
+ Preconditions.checkNotNull(domSource.getNode());
+ Preconditions.checkArgument(domSource.getNode().getNodeName().equals(qname.getLocalName()));
+ Preconditions.checkArgument(domSource.getNode().getNamespaceURI().equals(qname.getNamespace().toString()));
+ try {
+ // TODO can the transformer be a constant ? is it thread safe ?
+ final Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
+ // Writer has to be wrapped in a wrapper that ignores endDocument event
+ // EndDocument event forbids any other modification to the writer so a nested anyXml breaks serialization
+ transformer.transform(domSource, new StAXResult(new DelegateWriterNoEndDoc(writer)));
+ } catch (final TransformerException e) {
+ throw new IOException("Unable to transform anyXml(" + qname + ") value: " + value, e);
+ }
+ }
+ }
+
+ @Override
+ public final void startContainerNode(final NodeIdentifier name, final int childSizeHint,
+ final Map<QName, String> attributes) throws IOException {
+ startContainerNode(name, childSizeHint);
+ writeAttributes(attributes);
+ }
+
+ @Override
+ public final void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint,
+ final Map<QName, String> attributes) throws IOException {
+ startYangModeledAnyXmlNode(name, childSizeHint);
+ writeAttributes(attributes);
+ }
+
+ @Override
+ public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint,
+ final Map<QName, String> attributes) throws IOException {
+ startUnkeyedListItem(name, childSizeHint);
+ writeAttributes(attributes);
+ }
+
+ @Override
+ public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint,
+ final Map<QName, String> attributes) throws IOException {
+ startMapEntryNode(identifier, childSizeHint);
+ writeAttributes(attributes);
+ }
+
+ @Override
+ public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
+ startList(name);
+ }
+
+ @Override
+ public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ startListItem(name);
+ }
+
+ @Override
+ public final void startMapNode(final NodeIdentifier name, final int childSizeHint) {
+ startList(name);
+ }
+
+ @Override
+ public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+ throws IOException {
+ startListItem(identifier);
+ }
+
+ @Override
+ public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
+ startList(name);
+ }
+
+ public static String toString(final Element xml) {
+ try {
+ final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+ final StreamResult result = new StreamResult(new StringWriter());
+ final DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+
+ @Override
+ public final void endNode() throws IOException {
+ try {
+ endNode(writer);
+ } catch (XMLStreamException e) {
+ throw new IOException("Failed to end element", e);
+ }
+ }
+
+ @Override
+ public final void close() throws IOException {
+ try {
+ writer.close();
+ } catch (XMLStreamException e) {
+ throw new IOException("Failed to close writer", e);
+ }
+ }
+
+ @Override
+ public final void flush() throws IOException {
+ try {
+ writer.flush();
+ } catch (XMLStreamException e) {
+ throw new IOException("Failed to flush writer", e);
+ }
+ }
+
+ /**
+ * Delegate writer that ignores writeEndDocument event. Used for AnyXml serialization.
+ */
+ private static final class DelegateWriterNoEndDoc implements XMLStreamWriter {
+ private final XMLStreamWriter writer;
+
+ public DelegateWriterNoEndDoc(final XMLStreamWriter writer) {
+ this.writer = writer;
+ }
+
+ @Override
+ public void writeStartElement(final String localName) throws XMLStreamException {
+ writer.writeStartElement(localName);
+ }
+
+ @Override
+ public void writeStartElement(final String namespaceURI, final String localName) throws XMLStreamException {
+ writer.writeStartElement(namespaceURI, localName);
+ }
+
+ @Override
+ public void writeStartElement(final String prefix, final String localName, final String namespaceURI)
+ throws XMLStreamException {
+ writer.writeStartElement(prefix, localName, namespaceURI);
+ }
+
+ @Override
+ public void writeEmptyElement(final String namespaceURI, final String localName) throws XMLStreamException {
+ writer.writeEmptyElement(namespaceURI, localName);
+ }
+
+ @Override
+ public void writeEmptyElement(final String prefix, final String localName, final String namespaceURI)
+ throws XMLStreamException {
+ writer.writeEmptyElement(prefix, localName, namespaceURI);
+ }
+
+ @Override
+ public void writeEmptyElement(final String localName) throws XMLStreamException {
+ writer.writeEmptyElement(localName);
+ }
+
+ @Override
+ public void writeEndElement() throws XMLStreamException {
+ writer.writeEndElement();
+
+ }
+
+ @Override
+ public void writeEndDocument() throws XMLStreamException {
+ // End document is disabled
+ }
+
+ @Override
+ public void close() throws XMLStreamException {
+ writer.close();
+ }
+
+ @Override
+ public void flush() throws XMLStreamException {
+ writer.flush();
+ }
+
+ @Override
+ public void writeAttribute(final String localName, final String value) throws XMLStreamException {
+ writer.writeAttribute(localName, value);
+ }
+
+ @Override
+ public void writeAttribute(final String prefix, final String namespaceURI, final String localName,
+ final String value) throws XMLStreamException {
+ writer.writeAttribute(prefix, namespaceURI, localName, value);
+ }
+
+ @Override
+ public void writeAttribute(final String namespaceURI, final String localName, final String value)
+ throws XMLStreamException {
+ writer.writeAttribute(namespaceURI, localName, value);
+ }
+
+ @Override
+ public void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException {
+ // Workaround for default namespace
+ // If a namespace is not prefixed, it is is still treated as prefix namespace.
+ // This results in the NamespaceSupport class ignoring the namespace since xmlns is not a valid prefix
+ // Write the namespace at least as an attribute
+ // TODO this is a hotfix, the transformer itself should write namespaces passing the namespace
+ // in writeStartElement method
+ if (prefix.equals("xml") || prefix.equals("xmlns")) {
+ writer.writeAttribute(prefix, namespaceURI);
+ } else {
+ writer.writeNamespace(prefix, namespaceURI);
+ }
+ }
+
+ @Override
+ public void writeDefaultNamespace(final String namespaceURI) throws XMLStreamException {
+ writer.writeDefaultNamespace(namespaceURI);
+ }
+
+ @Override
+ public void writeComment(final String data) throws XMLStreamException {
+ writer.writeComment(data);
+ }
+
+ @Override
+ public void writeProcessingInstruction(final String target) throws XMLStreamException {
+ writer.writeProcessingInstruction(target);
+ }
+
+ @Override
+ public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException {
+ writer.writeProcessingInstruction(target, data);
+ }
+
+ @Override
+ public void writeCData(final String data) throws XMLStreamException {
+ writer.writeCData(data);
+ }
+
+ @Override
+ public void writeDTD(final String dtd) throws XMLStreamException {
+ writer.writeDTD(dtd);
+ }
+
+ @Override
+ public void writeEntityRef(final String name) throws XMLStreamException {
+ writer.writeEntityRef(name);
+ }
+
+ @Override
+ public void writeStartDocument() throws XMLStreamException {
+ }
+
+ @Override
+ public void writeStartDocument(final String version) throws XMLStreamException {
+ }
+
+ @Override
+ public void writeStartDocument(final String encoding, final String version) throws XMLStreamException {
+ }
+
+ @Override
+ public void writeCharacters(final String text) throws XMLStreamException {
+ writer.writeCharacters(text);
+ }
+
+ @Override
+ public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException {
+ writer.writeCharacters(text, start, len);
+ }
+
+ @Override
+ public String getPrefix(final String uri) throws XMLStreamException {
+ return writer.getPrefix(uri);
+ }
+
+ @Override
+ public void setPrefix(final String prefix, final String uri) throws XMLStreamException {
+ // Disabled since it causes exceptions in the underlying writer
+ }
+
+ @Override
+ public void setDefaultNamespace(final String uri) throws XMLStreamException {
+ writer.setDefaultNamespace(uri);
+ }
+
+ @Override
+ public void setNamespaceContext(final NamespaceContext context) throws XMLStreamException {
+ writer.setNamespaceContext(context);
+ }
+
+ @Override
+ public NamespaceContext getNamespaceContext() {
+ return writer.getNamespaceContext();
+ }
+
+ @Override
+ public Object getProperty(final String name) {
+ return writer.getProperty(name);
+ }
+ }
+}
*
* @param writer XMLStreamWriter
* @param value value which will be serialized to the writer
+ * @throws XMLStreamException from {@link XMLStreamWriter}
*/
void serializeToWriter(XMLStreamWriter writer, T value) throws XMLStreamException;
}
package org.opendaylight.yangtools.yang.data.codec.xml;
+import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Map.Entry;
+import javax.annotation.concurrent.ThreadSafe;
+import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
-import org.opendaylight.yangtools.yang.model.util.type.DerivedTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@Beta
+@ThreadSafe
public final class XmlCodecFactory {
private static final Logger LOG = LoggerFactory.getLogger(XmlCodecFactory.class);
}
};
- private final LoadingCache<DataSchemaNode, XmlCodec<?>> codecs =
- CacheBuilder.newBuilder().softValues().build(new CacheLoader<DataSchemaNode, XmlCodec<?>>() {
+ private final LoadingCache<Entry<DataSchemaNode, NamespaceContext>, XmlCodec<?>> codecs =
+ CacheBuilder.newBuilder().softValues().build(
+ new CacheLoader<Entry<DataSchemaNode, NamespaceContext>, XmlCodec<?>>() {
@Override
- public XmlCodec<?> load(final DataSchemaNode key) throws Exception {
+ public XmlCodec<?> load(final Entry<DataSchemaNode, NamespaceContext> schemaNodeAndNamespaceCtxPair)
+ throws Exception {
+ final DataSchemaNode schemaNode = schemaNodeAndNamespaceCtxPair.getKey();
final TypeDefinition<?> type;
- if (key instanceof LeafSchemaNode) {
- type = ((LeafSchemaNode) key).getType();
- } else if (key instanceof LeafListSchemaNode) {
- type = ((LeafListSchemaNode) key).getType();
+ if (schemaNode instanceof LeafSchemaNode) {
+ type = ((LeafSchemaNode) schemaNode).getType();
+ } else if (schemaNode instanceof LeafListSchemaNode) {
+ type = ((LeafListSchemaNode) schemaNode).getType();
} else {
- throw new IllegalArgumentException("Not supported node type " + key.getClass().getName());
+ throw new IllegalArgumentException("Not supported node type " + schemaNode.getClass().getName());
}
- return createCodec(key,type);
+ return createCodec(schemaNode,type, schemaNodeAndNamespaceCtxPair.getValue());
}
});
private final SchemaContext schemaContext;
- private final XmlCodec<YangInstanceIdentifier> iidCodec;
private XmlCodecFactory(final SchemaContext context) {
this.schemaContext = Preconditions.checkNotNull(context);
- iidCodec = new XmlStringInstanceIdentifierCodec(context, this);
}
/**
return new XmlCodecFactory(context);
}
- private XmlCodec<?> createCodec(final DataSchemaNode key, final TypeDefinition<?> type) {
- final TypeDefinition<?> normalizedType = DerivedTypes.derivedTypeBuilder(type, type.getPath()).build();
- if (normalizedType instanceof LeafrefTypeDefinition) {
- return createReferencedTypeCodec(key, (LeafrefTypeDefinition) normalizedType);
- } else if (normalizedType instanceof IdentityrefTypeDefinition) {
+ private XmlCodec<?> createCodec(final DataSchemaNode key, final TypeDefinition<?> type,
+ final NamespaceContext namespaceContext) {
+ if (type instanceof LeafrefTypeDefinition) {
+ return createReferencedTypeCodec(key, (LeafrefTypeDefinition) type, namespaceContext);
+ } else if (type instanceof IdentityrefTypeDefinition) {
final XmlCodec<?> xmlStringIdentityrefCodec =
- new XmlStringIdentityrefCodec(schemaContext, key.getQName().getModule());
+ new XmlStringIdentityrefCodec(schemaContext, key.getQName().getModule(), namespaceContext);
return xmlStringIdentityrefCodec;
}
- return createFromSimpleType(normalizedType);
+ return createFromSimpleType(type, namespaceContext);
}
- private XmlCodec<?> createReferencedTypeCodec(final DataSchemaNode schema, final LeafrefTypeDefinition type) {
+ private XmlCodec<?> createReferencedTypeCodec(final DataSchemaNode schema, final LeafrefTypeDefinition type,
+ final NamespaceContext namespaceContext) {
// FIXME: Verify if this does indeed support leafref of leafref
final TypeDefinition<?> referencedType =
SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
- return createCodec(schema, referencedType);
+ return createCodec(schema, referencedType, namespaceContext);
}
- private XmlCodec<?> createFromSimpleType(final TypeDefinition<?> type) {
+ private XmlCodec<?> createFromSimpleType(final TypeDefinition<?> type, final NamespaceContext namespaceContext) {
if (type instanceof InstanceIdentifierTypeDefinition) {
+ final XmlCodec<YangInstanceIdentifier> iidCodec = new XmlStringInstanceIdentifierCodec(schemaContext, this,
+ namespaceContext);
return iidCodec;
}
if (type instanceof EmptyTypeDefinition) {
return schemaContext;
}
- XmlCodec<?> codecFor(final DataSchemaNode schema) {
- return codecs.getUnchecked(schema);
+ XmlCodec<?> codecFor(final DataSchemaNode schema, final NamespaceContext namespaceContext) {
+ return codecs.getUnchecked(new SimpleImmutableEntry<>(schema, namespaceContext));
}
}
package org.opendaylight.yangtools.yang.data.codec.xml;
+import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import java.io.Closeable;
import java.io.Flushable;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
-import javax.xml.parsers.DocumentBuilder;
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.YangModeledAnyXmlSchemaNode;
import org.w3c.dom.Document;
+import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
* instances of the same element except for leaf-list and list entries. It also expects that the YANG-modeled data in
* the XML source are wrapped in a root element.
*/
+@Beta
+@NotThreadSafe
public final class XmlParserStream implements Closeable, Flushable {
+ private static final DocumentBuilderFactory FACTORY;
+
+ static {
+ DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
+ f.setNamespaceAware(true);
+ FACTORY = f;
+ }
- private String rootElement = null;
private final NormalizedNodeStreamWriter writer;
private final XmlCodecFactory codecs;
- private final SchemaContext schema;
private final DataSchemaNode parentNode;
private XmlParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext,
final DataSchemaNode parentNode) {
- this.schema = Preconditions.checkNotNull(schemaContext);
this.writer = Preconditions.checkNotNull(writer);
this.codecs = XmlCodecFactory.create(schemaContext);
this.parentNode = parentNode;
if (reader.hasNext()) {
final CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(parentNode);
reader.nextTag();
- rootElement = reader.getLocalName();
- read(reader, compositeNodeDataWithSchema);
+ read(reader, compositeNodeDataWithSchema, reader.getLocalName());
compositeNodeDataWithSchema.write(writer);
}
}
private static String readAnyXmlValue(final XMLStreamReader in) throws XMLStreamException {
- String result = "";
- String anyXmlElementName = in.getLocalName();
+ final StringBuilder sb = new StringBuilder();
+ final String anyXmlElementName = in.getLocalName();
+ sb.append('<').append(anyXmlElementName).append(" xmlns=\"").append(in.getNamespaceURI()).append("\">");
while (in.hasNext()) {
- int eventType = in.next();
+ final int eventType = in.next();
if (eventType == XMLStreamConstants.START_ELEMENT) {
- result += "<" + in.getLocalName() + ">";
+ sb.append('<').append(in.getLocalName()).append('>');
} else if (eventType == XMLStreamConstants.END_ELEMENT) {
+ sb.append("</").append(in.getLocalName()).append('>');
+
if (in.getLocalName().equals(anyXmlElementName)) {
break;
}
- result += "</" + in.getLocalName() + ">";
} else if (eventType == XMLStreamConstants.CHARACTERS) {
- result += in.getText();
+ sb.append(in.getText());
}
}
- return result;
+ return sb.toString();
}
- private void read(final XMLStreamReader in, final AbstractNodeDataWithSchema parent) throws XMLStreamException,
- URISyntaxException, ParserConfigurationException, SAXException, IOException {
- if (in.hasNext()) {
- if (parent instanceof LeafNodeDataWithSchema || parent instanceof LeafListEntryNodeDataWithSchema) {
- setValue(parent, in.getElementText().trim());
- in.nextTag();
- return;
- } else if (parent instanceof LeafListNodeDataWithSchema || parent instanceof ListNodeDataWithSchema) {
- String parentSchemaName = parent.getSchema().getQName().getLocalName();
- String xmlElementName = in.getLocalName();
- while (xmlElementName.equals(parentSchemaName)) {
- AbstractNodeDataWithSchema newChild = newEntryNode(parent);
- read(in, newChild);
- xmlElementName = in.getLocalName();
- }
+ private void read(final XMLStreamReader in, final AbstractNodeDataWithSchema parent, final String rootElement)
+ throws XMLStreamException, URISyntaxException, ParserConfigurationException, SAXException, IOException {
+ if (!in.hasNext()) {
+ return;
+ }
- return;
- } else if (parent instanceof AnyXmlNodeDataWithSchema) {
- setValue(parent, readAnyXmlValue(in));
- in.nextTag();
- return;
+ if (parent instanceof LeafNodeDataWithSchema || parent instanceof LeafListEntryNodeDataWithSchema) {
+ setValue(parent, in.getElementText().trim(), in.getNamespaceContext());
+ in.nextTag();
+ return;
+ }
+
+ if (parent instanceof LeafListNodeDataWithSchema || parent instanceof ListNodeDataWithSchema) {
+ String xmlElementName = in.getLocalName();
+ while (xmlElementName.equals(parent.getSchema().getQName().getLocalName())) {
+ read(in, newEntryNode(parent), rootElement);
+ xmlElementName = in.getLocalName();
}
- switch (in.nextTag()) {
- case XMLStreamConstants.START_ELEMENT:
- final Set<String> namesakes = new HashSet<>();
- while (in.hasNext()) {
- String xmlElementName = in.getLocalName();
- String xmlElementNamespace = in.getNamespaceURI();
-
- if (xmlElementName.equals(rootElement)) {
- break;
- }
-
- DataSchemaNode parentSchema = parent.getSchema();
- if (parentSchema instanceof YangModeledAnyXmlSchemaNode) {
- parentSchema = ((YangModeledAnyXmlSchemaNode) parentSchema).getSchemaOfAnyXmlData();
- }
-
- String parentSchemaName = parentSchema.getQName().getLocalName();
- if (parentSchemaName.equals(xmlElementName)
- && in.getEventType() == XMLStreamConstants.END_ELEMENT) {
- in.nextTag();
- break;
- }
-
- if (namesakes.contains(xmlElementName)) {
- int lineNumber = in.getLocation().getLineNumber();
- int columnNumber = in.getLocation().getColumnNumber();
- throw new IllegalStateException("Duplicate element \"" + xmlElementName + "\" in XML " +
- "input at: line " + lineNumber + " column " + columnNumber);
- }
- namesakes.add(xmlElementName);
-
- Deque<DataSchemaNode> childDataSchemaNodes = ParserStreamUtils.findSchemaNodeByNameAndNamespace(
- parentSchema, xmlElementName, new URI(xmlElementNamespace));
-
- if (childDataSchemaNodes.isEmpty()) {
- throw new IllegalStateException("Schema for node with name " + xmlElementName +
- " and namespace " + xmlElementNamespace + " doesn't exist.");
- }
-
- AbstractNodeDataWithSchema newChild =
- ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes);
-
- read(in, newChild);
+ return;
+ }
+
+ if (parent instanceof AnyXmlNodeDataWithSchema) {
+ setValue(parent, readAnyXmlValue(in), in.getNamespaceContext());
+ in.nextTag();
+ return;
+ }
+
+ switch (in.nextTag()) {
+ case XMLStreamConstants.START_ELEMENT:
+ final Set<String> namesakes = new HashSet<>();
+ while (in.hasNext()) {
+ final String xmlElementName = in.getLocalName();
+ if (rootElement.equals(xmlElementName)) {
+ break;
}
- break;
- case XMLStreamConstants.END_ELEMENT:
- in.nextTag();
- break;
- }
+
+ DataSchemaNode parentSchema = parent.getSchema();
+
+ final String parentSchemaName = parentSchema.getQName().getLocalName();
+ if (parentSchemaName.equals(xmlElementName) && in.getEventType() == XMLStreamConstants.END_ELEMENT) {
+ in.nextTag();
+ break;
+ }
+
+ if (parentSchema instanceof YangModeledAnyXmlSchemaNode) {
+ parentSchema = ((YangModeledAnyXmlSchemaNode) parentSchema).getSchemaOfAnyXmlData();
+ }
+
+ if (!namesakes.add(xmlElementName)) {
+ final Location loc = in.getLocation();
+ throw new IllegalStateException(String.format(
+ "Duplicate element \"%s\" in XML input at: line %s column %s", xmlElementName,
+ loc.getLineNumber(), loc.getColumnNumber()));
+ }
+
+ final String xmlElementNamespace = in.getNamespaceURI();
+ final Deque<DataSchemaNode> childDataSchemaNodes =
+ ParserStreamUtils.findSchemaNodeByNameAndNamespace(parentSchema, xmlElementName,
+ new URI(xmlElementNamespace));
+
+ Preconditions.checkState(!childDataSchemaNodes.isEmpty(),
+ "Schema for node with name %s and namespace %s doesn't exist.",
+ xmlElementName, xmlElementNamespace);
+
+ read(in, ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes), rootElement);
+ }
+ break;
+ case XMLStreamConstants.END_ELEMENT:
+ in.nextTag();
+ break;
}
}
- private void setValue(final AbstractNodeDataWithSchema parent, final String value) throws
- ParserConfigurationException, SAXException, IOException {
+ private void setValue(final AbstractNodeDataWithSchema parent, final String value, final NamespaceContext nsContext)
+ throws ParserConfigurationException, SAXException, IOException {
Preconditions.checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type",
parent.getSchema().getQName());
final SimpleNodeDataWithSchema parentSimpleNode = (SimpleNodeDataWithSchema) parent;
Preconditions.checkArgument(parentSimpleNode.getValue() == null, "Node '%s' has already set its value to '%s'",
parentSimpleNode.getSchema().getQName(), parentSimpleNode.getValue());
- final Object translatedValue = translateValueByType(value, parentSimpleNode.getSchema());
- parentSimpleNode.setValue(translatedValue);
+ parentSimpleNode.setValue(translateValueByType(value, parentSimpleNode.getSchema(), nsContext));
}
- private Object translateValueByType(final String value, final DataSchemaNode node) throws IOException,
- SAXException, ParserConfigurationException {
+ private Object translateValueByType(final String value, final DataSchemaNode node, final NamespaceContext namespaceCtx)
+ throws IOException, SAXException, ParserConfigurationException {
if (node instanceof AnyXmlSchemaNode) {
/*
* FIXME: Figure out some YANG extension dispatch, which will
* reuse JSON parsing or XML parsing - anyxml is not well-defined in
* JSON.
*/
- DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
- Document doc = db.parse( new InputSource(new StringReader(value)));
+ final Document doc = FACTORY.newDocumentBuilder().parse(new InputSource(new StringReader(value)));
doc.normalize();
- DOMSource anyXmlValueSource = new DOMSource(doc);
- return anyXmlValueSource;
+ return new DOMSource(doc.getDocumentElement());
+ } else {
+ return codecs.codecFor(node, namespaceCtx).deserialize(value);
}
- return codecs.codecFor(node).deserialize(value);
}
private static AbstractNodeDataWithSchema newEntryNode(final AbstractNodeDataWithSchema parent) {
- AbstractNodeDataWithSchema newChild;
+ final AbstractNodeDataWithSchema newChild;
if (parent instanceof ListNodeDataWithSchema) {
newChild = new ListEntryNodeDataWithSchema(parent.getSchema());
} else {
--- /dev/null
+/*
+ * Copyright (c) 2015 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.codec.xml;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
+import java.net.URI;
+import java.util.Map.Entry;
+import javax.annotation.Nonnull;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class
+ * by no means final and subject to change as more functionality is centralized here.
+ */
+class XmlStreamUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class);
+ private final Optional<SchemaContext> schemaContext;
+
+ private XmlStreamUtils(final SchemaContext schemaContext) {
+ this.schemaContext = Optional.fromNullable(schemaContext);
+ }
+
+ static XmlStreamUtils create(final SchemaContext schemaContext) {
+ return new XmlStreamUtils(schemaContext);
+ }
+
+ @VisibleForTesting
+ static void writeAttribute(final XMLStreamWriter writer, final Entry<QName, String> attribute,
+ final RandomPrefix randomPrefix) throws XMLStreamException {
+ final QName key = attribute.getKey();
+ final String prefix = randomPrefix.encodePrefix(key.getNamespace());
+ writer.writeAttribute("xmlns:" + prefix, key.getNamespace().toString());
+ writer.writeAttribute(prefix, key.getNamespace().toString(), key.getLocalName(), attribute.getValue());
+ }
+
+ /**
+ * Write a value into a XML stream writer. This method assumes the start and end of element is
+ * emitted by the caller.
+ *
+ * @param writer XML Stream writer
+ * @param schemaNode Schema node that describes the value
+ * @param value data value
+ * @param parent optional parameter of a module QName owning the leaf definition
+ * @throws XMLStreamException if an encoding problem occurs
+ */
+ void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode,
+ final Object value, final Optional<QNameModule> parent) throws XMLStreamException {
+ if (value == null) {
+ LOG.debug("Value of {}:{} is null, not encoding it", schemaNode.getQName().getNamespace(),
+ schemaNode.getQName().getLocalName());
+ return;
+ }
+
+ Preconditions.checkArgument(schemaNode instanceof LeafSchemaNode || schemaNode instanceof LeafListSchemaNode,
+ "Unable to write value for node %s, only nodes of type: leaf and leaf-list can be written at this point",
+ schemaNode.getQName());
+
+ TypeDefinition<?> type = schemaNode instanceof LeafSchemaNode ?
+ ((LeafSchemaNode) schemaNode).getType():
+ ((LeafListSchemaNode) schemaNode).getType();
+
+ if (schemaContext.isPresent() && type instanceof LeafrefTypeDefinition) {
+ LeafrefTypeDefinition leafrefTypeDefinition = (LeafrefTypeDefinition) type;
+ type = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypeDefinition, schemaContext.get(), schemaNode);
+ Verify.verifyNotNull(type, "Unable to find base type for leafref node '%s'.", schemaNode.getPath());
+ }
+
+ writeValue(writer, type, value, parent);
+ }
+
+ void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode,
+ final Object value) throws XMLStreamException {
+ writeValue(writer, schemaNode, value, Optional.absent());
+ }
+
+ void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode,
+ final Object value, final QNameModule parent) throws XMLStreamException {
+ writeValue(writer, schemaNode, value, Optional.of(parent));
+ }
+
+ /**
+ * Write a value into a XML stream writer. This method assumes the start and end of element is
+ * emitted by the caller.
+ *
+ * @param writer XML Stream writer
+ * @param type data type. In case of leaf ref this should be the type of leaf being referenced
+ * @param value data value
+ * @param parent optional parameter of a module QName owning the leaf definition
+ * @throws XMLStreamException if an encoding problem occurs
+ */
+ void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition<?> type,
+ final Object value, final Optional<QNameModule> parent) throws XMLStreamException {
+ if (value == null) {
+ LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(),
+ type.getQName().getLocalName());
+ return;
+ }
+
+ if (type instanceof IdentityrefTypeDefinition) {
+ if (parent.isPresent()) {
+ write(writer, (IdentityrefTypeDefinition) type, value, parent);
+ } else {
+ write(writer, (IdentityrefTypeDefinition) type, value, Optional.absent());
+ }
+ } else if (type instanceof InstanceIdentifierTypeDefinition) {
+ write(writer, (InstanceIdentifierTypeDefinition) type, value);
+ } else {
+ final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
+ String text;
+ if (codec != null) {
+ try {
+ text = codec.serialize(value);
+ } catch (ClassCastException e) {
+ LOG.warn("Provided node value {} did not have type {} required by mapping. Using stream instead.",
+ value, type, e);
+ text = String.valueOf(value);
+ }
+ } else {
+ LOG.warn("Failed to find codec for {}, falling back to using stream", type);
+ text = String.valueOf(value);
+ }
+ writer.writeCharacters(text);
+ }
+ }
+
+ void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition<?> type,
+ final Object value, final QNameModule parent) throws XMLStreamException {
+ writeValue(writer, type, value, Optional.of(parent));
+ }
+
+ void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition<?> type,
+ final Object value) throws XMLStreamException {
+ writeValue(writer, type, value, Optional.absent());
+ }
+
+ @VisibleForTesting
+ static void write(@Nonnull final XMLStreamWriter writer, @Nonnull final IdentityrefTypeDefinition type,
+ @Nonnull final Object value, final Optional<QNameModule> parent) throws XMLStreamException {
+ if (value instanceof QName) {
+ final QName qname = (QName) value;
+ final String prefix = "x";
+
+ //in case parent is present and same as element namespace write value without namespace
+ if (parent.isPresent() && qname.getNamespace().equals(parent.get().getNamespace())){
+ writer.writeCharacters(qname.getLocalName());
+ } else {
+ final String ns = qname.getNamespace().toString();
+ writer.writeNamespace(prefix, ns);
+ writer.writeCharacters(prefix + ':' + qname.getLocalName());
+ }
+
+ } else {
+ LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(),
+ type.getQName().getLocalName(), value.getClass());
+ writer.writeCharacters(String.valueOf(value));
+ }
+ }
+
+ private void write(@Nonnull final XMLStreamWriter writer, @Nonnull final InstanceIdentifierTypeDefinition type,
+ @Nonnull final Object value) throws XMLStreamException {
+ if (value instanceof YangInstanceIdentifier) {
+ writeInstanceIdentifier(writer, (YangInstanceIdentifier)value);
+ } else {
+ LOG.warn("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(),
+ type.getQName().getLocalName(), value.getClass());
+ writer.writeCharacters(String.valueOf(value));
+ }
+ }
+
+ void writeInstanceIdentifier(final XMLStreamWriter writer, final YangInstanceIdentifier value)
+ throws XMLStreamException {
+ if (schemaContext.isPresent()) {
+ RandomPrefixInstanceIdentifierSerializer iiCodec =
+ new RandomPrefixInstanceIdentifierSerializer(schemaContext.get());
+ String serializedValue = iiCodec.serialize(value);
+ writeNamespaceDeclarations(writer, iiCodec.getPrefixes());
+ writer.writeCharacters(serializedValue);
+ } else {
+ LOG.warn("Schema context not present in {}, serializing {} without schema.",this,value);
+ writeInstanceIdentifier(writer, value);
+ }
+ }
+
+ private static void writeNamespaceDeclarations(final XMLStreamWriter writer,
+ final Iterable<Entry<URI, String>> prefixes) throws XMLStreamException {
+ for (Entry<URI, String> e: prefixes) {
+ writer.writeNamespace(e.getValue(), e.getKey().toString());
+ }
+ }
+}
import com.google.common.base.Preconditions;
import java.net.URI;
+import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.opendaylight.yangtools.yang.common.QName;
private final SchemaContext context;
private final QNameModule parentModuleQname;
+ private final NamespaceContext namespaceContext;
- XmlStringIdentityrefCodec(final SchemaContext context, final QNameModule parentModule) {
+ XmlStringIdentityrefCodec(final SchemaContext context, final QNameModule parentModule,
+ final NamespaceContext namespaceContext) {
this.context = Preconditions.checkNotNull(context);
this.parentModuleQname = Preconditions.checkNotNull(parentModule);
+ this.namespaceContext = Preconditions.checkNotNull(namespaceContext);
}
@Override
return context.findModuleByNamespaceAndRevision(parentModuleQname.getNamespace(),
parentModuleQname.getRevision());
} else {
- return context.findModuleByName(prefix, null);
+ final String prefixedNS = namespaceContext.getNamespaceURI(prefix);
+ return context.findModuleByNamespaceAndRevision(URI.create(prefixedNS), null);
}
}
* @param value QName
*/
@Override
- public void serializeToWriter(XMLStreamWriter writer, QName value) throws XMLStreamException {
+ public void serializeToWriter(final XMLStreamWriter writer, final QName value) throws XMLStreamException {
writer.writeCharacters(serialize(value));
}
}
import com.google.common.base.Preconditions;
import java.net.URI;
+import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
private final DataSchemaContextTree dataContextTree;
private final XmlCodecFactory codecFactory;
private final SchemaContext context;
+ private final NamespaceContext namespaceContext;
- XmlStringInstanceIdentifierCodec(final SchemaContext context, final XmlCodecFactory jsonCodecFactory) {
+ XmlStringInstanceIdentifierCodec(final SchemaContext context, final XmlCodecFactory xmlCodecFactory,
+ final NamespaceContext namespaceContext) {
this.context = Preconditions.checkNotNull(context);
this.dataContextTree = DataSchemaContextTree.from(context);
- this.codecFactory = Preconditions.checkNotNull(jsonCodecFactory);
+ this.codecFactory = Preconditions.checkNotNull(xmlCodecFactory);
+ this.namespaceContext = Preconditions.checkNotNull(namespaceContext);
}
@Override
protected Module moduleForPrefix(final String prefix) {
- return context.findModuleByName(prefix, null);
+ final String prefixedNS = namespaceContext.getNamespaceURI(prefix);
+ return context.findModuleByNamespaceAndRevision(URI.create(prefixedNS), null);
}
@Override
protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) {
Preconditions.checkNotNull(schemaNode, "schemaNode cannot be null");
Preconditions.checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode");
- final XmlCodec<?> objectXmlCodec = codecFactory.codecFor(schemaNode);
+ final XmlCodec<?> objectXmlCodec = codecFactory.codecFor(schemaNode, namespaceContext);
return objectXmlCodec.deserialize(value);
}
--- /dev/null
+/*
+ * 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.codec.xml;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import java.net.URI;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+
+public class Bug5396Test {
+
+ private QNameModule fooModuleQName;
+ private SchemaContext schemaContext;
+
+ @Before
+ public void Init() throws Exception {
+ fooModuleQName = QNameModule.create(new URI("foo"), SimpleDateFormatUtil.getRevisionFormat().parse("2016-03-22"));
+
+ CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ reactor.addSource(new YangStatementSourceImpl("/bug5396/yang/foo.yang", false));
+
+ schemaContext = reactor.buildEffective();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testInputXML("/bug5396/xml/foo.xml", "dp1o34");
+ testInputXML("/bug5396/xml/foo2.xml", "dp0s3f9");
+ testInputXML("/bug5396/xml/foo3.xml", "dp09P1p2s3");
+ testInputXML("/bug5396/xml/foo4.xml", "dp0p3p1");
+ testInputXML("/bug5396/xml/foo5.xml", "dp0s3");
+
+ try {
+ testInputXML("/bug5396/xml/invalid-foo.xml", null);
+ fail("Test should fail due to invalid input string");
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.getMessage().startsWith("Invalid value \"dp09P1p2s1234\" for union type."));
+ }
+ }
+
+ private void testInputXML(String xmlPath, String expectedValue) throws Exception {
+ final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream(xmlPath);
+
+ final XMLInputFactory factory = XMLInputFactory.newInstance();
+ final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream);
+
+ final NormalizedNodeResult result = new NormalizedNodeResult();
+
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+ final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext);
+ xmlParser.parse(reader);
+
+ assertNotNull(result.getResult());
+ assertTrue(result.getResult() instanceof ContainerNode);
+ final ContainerNode rootContainer = (ContainerNode) result.getResult();
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> myLeaf = rootContainer.getChild(new NodeIdentifier(
+ QName.create(fooModuleQName, "my-leaf")));
+ assertTrue(myLeaf.orNull() instanceof LeafNode);
+
+ assertEquals(expectedValue, myLeaf.get().getValue());
+ }
+}
--- /dev/null
+/*
+ * 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.codec.xml;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.io.BaseEncoding;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class Bug5446Test extends XMLTestCase {
+ private static final XMLOutputFactory XML_FACTORY;
+ private static final DocumentBuilderFactory BUILDERFACTORY;
+
+ static {
+ XML_FACTORY = XMLOutputFactory.newFactory();
+ XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
+
+ BUILDERFACTORY = DocumentBuilderFactory.newInstance();
+ BUILDERFACTORY.setNamespaceAware(true);
+ BUILDERFACTORY.setCoalescing(true);
+ BUILDERFACTORY.setIgnoringElementContentWhitespace(true);
+ BUILDERFACTORY.setIgnoringComments(true);
+ }
+
+ private QNameModule fooModuleQName;
+ private QName rootQName;
+ private QName ipAddressQName;
+ private SchemaContext schemaContext;
+
+ public Bug5446Test() throws Exception {
+ fooModuleQName = QNameModule.create(new URI("foo"), SimpleDateFormatUtil.getRevisionFormat()
+ .parse("2015-11-05"));
+ rootQName = QName.create(fooModuleQName, "root");
+ ipAddressQName = QName.create(fooModuleQName, "ip-address");
+
+ CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ reactor.addSource(new YangStatementSourceImpl("/bug5446/yang/foo.yang", false));
+
+ schemaContext = reactor.buildEffective();
+ }
+
+ @Test
+ public void test() throws Exception {
+ final Document doc = loadDocument("/bug5446/xml/foo.xml");
+
+ final ContainerNode docNode = createDocNode();
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> root = docNode.getChild(new NodeIdentifier(rootQName));
+ assertTrue(root.orNull() instanceof ContainerNode);
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> child = ((ContainerNode) root.orNull())
+ .getChild(new NodeIdentifier(ipAddressQName));
+ assertTrue(child.orNull() instanceof LeafNode);
+ LeafNode<?> ipAdress = (LeafNode<?>) child.get();
+
+ Object value = ipAdress.getValue();
+ assertTrue(value instanceof byte[]);
+ assertEquals("fwAAAQ==", BaseEncoding.base64().encode((byte[]) value));
+
+ DOMResult serializationResult = writeNormalizedNode(docNode, schemaContext);
+ assertNotNull(serializationResult);
+
+ XMLUnit.setIgnoreWhitespace(true);
+ XMLUnit.setIgnoreComments(true);
+ XMLUnit.setIgnoreAttributeOrder(true);
+ XMLUnit.setNormalize(true);
+
+ String expectedXMLString = toString(doc.getDocumentElement().getElementsByTagName("root").item(0));
+ String serializationResultXMLString = toString(serializationResult.getNode());
+
+ assertXMLEqual(expectedXMLString, serializationResultXMLString);
+ }
+
+ private ContainerNode createDocNode() {
+ LeafNode<byte[]> ipAddress = ImmutableNodes.leafNode(ipAddressQName, BaseEncoding.base64().decode("fwAAAQ=="));
+ ContainerNode root = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(rootQName))
+ .withChild(ipAddress).build();
+ return ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(rootQName)).withChild(root)
+ .build();
+ }
+
+ private static DOMResult writeNormalizedNode(final ContainerNode normalized, final SchemaContext context)
+ throws IOException, XMLStreamException {
+ final Document doc = getDocument();
+ final DOMResult result = new DOMResult(doc);
+ NormalizedNodeWriter normalizedNodeWriter = null;
+ NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+ XMLStreamWriter writer = null;
+ try {
+ writer = XML_FACTORY.createXMLStreamWriter(result);
+ normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context);
+ normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+ for (NormalizedNode<?, ?> child : normalized.getValue()) {
+ normalizedNodeWriter.write(child);
+ }
+
+ normalizedNodeWriter.flush();
+ } finally {
+ if (normalizedNodeWriter != null) {
+ normalizedNodeWriter.close();
+ }
+ if (normalizedNodeStreamWriter != null) {
+ normalizedNodeStreamWriter.close();
+ }
+ if (writer != null) {
+ writer.close();
+ }
+ }
+
+ return result;
+ }
+
+ private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+ final InputStream resourceAsStream = Bug5446Test.class.getResourceAsStream(xmlPath);
+ final Document currentConfigElement = readXmlToDocument(resourceAsStream);
+ Preconditions.checkNotNull(currentConfigElement);
+ return currentConfigElement;
+ }
+
+ private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+ final DocumentBuilder dBuilder;
+ try {
+ dBuilder = BUILDERFACTORY.newDocumentBuilder();
+ } catch (final ParserConfigurationException e) {
+ throw new RuntimeException("Failed to parse XML document", e);
+ }
+ final Document doc = dBuilder.parse(xmlContent);
+
+ doc.getDocumentElement().normalize();
+ return doc;
+ }
+
+ private static String toString(final Node xml) {
+ try {
+ final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ final StreamResult result = new StreamResult(new StringWriter());
+ final DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+
+ private static Document getDocument() {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ Document doc = null;
+ try {
+ DocumentBuilder bob = dbf.newDocumentBuilder();
+ doc = bob.newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ return doc;
+ }
+}
--- /dev/null
+/*
+ * 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.codec.xml;
+
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.augmentationBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.ElementNameAndTextQualifier;
+import org.custommonkey.xmlunit.IgnoreTextAndAttributeValuesDifferenceListener;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class NormalizedNodeXmlTranslationTest {
+ private final SchemaContext schema;
+
+ @Parameterized.Parameters()
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+ { "/schema/augment_choice_hell.yang", "/schema/augment_choice_hell_ok.xml", augmentChoiceHell() },
+ { "/schema/augment_choice_hell.yang", "/schema/augment_choice_hell_ok2.xml", null },
+ { "/schema/augment_choice_hell.yang", "/schema/augment_choice_hell_ok3.xml", augmentChoiceHell2() },
+ { "/schema/test.yang", "/schema/simple.xml", null },
+ { "/schema/test.yang", "/schema/simple2.xml", null },
+ // TODO check attributes
+ { "/schema/test.yang", "/schema/simple_xml_with_attributes.xml", withAttributes() }
+ });
+ }
+
+ private static final String NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:test";
+ private static final Date revision;
+ static {
+ try {
+ revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-03-13");
+ } catch (final ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static ContainerNode augmentChoiceHell2() {
+ final YangInstanceIdentifier.NodeIdentifier container = getNodeIdentifier("container");
+ final QName augmentChoice1QName = QName.create(container.getNodeType(), "augment-choice1");
+ final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
+ final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
+ final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
+
+ final YangInstanceIdentifier.AugmentationIdentifier aug1Id = new YangInstanceIdentifier.AugmentationIdentifier(
+ Sets.newHashSet(augmentChoice1QName));
+ final YangInstanceIdentifier.AugmentationIdentifier aug2Id = new YangInstanceIdentifier.AugmentationIdentifier(
+ Sets.newHashSet(augmentChoice2QName));
+ final YangInstanceIdentifier.NodeIdentifier augmentChoice1Id = new YangInstanceIdentifier.NodeIdentifier(
+ augmentChoice1QName);
+ final YangInstanceIdentifier.NodeIdentifier augmentChoice2Id = new YangInstanceIdentifier.NodeIdentifier(
+ augmentChoice2QName);
+ final YangInstanceIdentifier.NodeIdentifier containerId = new YangInstanceIdentifier.NodeIdentifier(
+ containerQName);
+
+ return containerBuilder().withNodeIdentifier(container)
+ .withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
+ .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
+ .withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
+ .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id)
+ .withChild(containerBuilder().withNodeIdentifier(containerId)
+ .withChild(leafNode(leafQName, "leaf-value"))
+ .build())
+ .build())
+ .build())
+ .build())
+ .build()).build();
+ }
+
+ private static ContainerNode withAttributes() {
+ final DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> b = containerBuilder();
+ b.withNodeIdentifier(getNodeIdentifier("container"));
+
+ final CollectionNodeBuilder<MapEntryNode, MapNode> listBuilder = Builders.mapBuilder().withNodeIdentifier(
+ getNodeIdentifier("list"));
+
+ final Map<QName, Object> predicates = Maps.newHashMap();
+ predicates.put(getNodeIdentifier("uint32InList").getNodeType(), 3L);
+
+ final DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> list1Builder = Builders
+ .mapEntryBuilder().withNodeIdentifier(
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+ getNodeIdentifier("list").getNodeType(), predicates));
+ final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> uint32InListBuilder = Builders
+ .leafBuilder().withNodeIdentifier(getNodeIdentifier("uint32InList"));
+
+ list1Builder.withChild(uint32InListBuilder.withValue(3L).build());
+
+ listBuilder.withChild(list1Builder.build());
+ b.withChild(listBuilder.build());
+
+ final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> booleanBuilder = Builders
+ .leafBuilder().withNodeIdentifier(getNodeIdentifier("boolean"));
+ booleanBuilder.withValue(false);
+ b.withChild(booleanBuilder.build());
+
+ final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafListBuilder = Builders.leafSetBuilder()
+ .withNodeIdentifier(getNodeIdentifier("leafList"));
+
+ final NormalizedNodeBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>> leafList1Builder = Builders
+ .leafSetEntryBuilder().withNodeIdentifier(
+ new YangInstanceIdentifier.NodeWithValue(getNodeIdentifier("leafList").getNodeType(), "a"));
+
+ leafList1Builder.withValue("a");
+
+ leafListBuilder.withChild(leafList1Builder.build());
+ b.withChild(leafListBuilder.build());
+
+ return b.build();
+ }
+
+ private static ContainerNode augmentChoiceHell() {
+
+ final DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> b = containerBuilder();
+ b.withNodeIdentifier(getNodeIdentifier("container"));
+
+ b.withChild(choiceBuilder()
+ .withNodeIdentifier(getNodeIdentifier("ch2"))
+ .withChild(
+ Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c2Leaf")).withValue("2").build())
+ .withChild(
+ choiceBuilder()
+ .withNodeIdentifier(getNodeIdentifier("c2DeepChoice"))
+ .withChild(
+ Builders.leafBuilder()
+ .withNodeIdentifier(getNodeIdentifier("c2DeepChoiceCase1Leaf2"))
+ .withValue("2").build()).build()).build());
+
+ b.withChild(choiceBuilder()
+ .withNodeIdentifier(getNodeIdentifier("ch3"))
+ .withChild(
+ Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c3Leaf")).withValue("3").build())
+ .build());
+
+ b.withChild(augmentationBuilder()
+ .withNodeIdentifier(getAugmentIdentifier("augLeaf"))
+ .withChild(
+ Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("augLeaf")).withValue("augment")
+ .build()).build());
+
+ b.withChild(augmentationBuilder()
+ .withNodeIdentifier(getAugmentIdentifier("ch"))
+ .withChild(
+ choiceBuilder()
+ .withNodeIdentifier(getNodeIdentifier("ch"))
+ .withChild(
+ Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c1Leaf"))
+ .withValue("1").build())
+ .withChild(
+ augmentationBuilder()
+ .withNodeIdentifier(
+ getAugmentIdentifier("c1Leaf_AnotherAugment", "deepChoice"))
+ .withChild(
+ Builders.leafBuilder()
+ .withNodeIdentifier(
+ getNodeIdentifier("c1Leaf_AnotherAugment"))
+ .withValue("1").build())
+ .withChild(
+ choiceBuilder()
+ .withNodeIdentifier(getNodeIdentifier("deepChoice"))
+ .withChild(
+ Builders.leafBuilder()
+ .withNodeIdentifier(
+ getNodeIdentifier("deepLeafc1"))
+ .withValue("1").build()).build())
+ .build()).build()).build());
+
+ return b.build();
+ }
+
+ private static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(final String localName) {
+ return new YangInstanceIdentifier.NodeIdentifier(QName.create(URI.create(NAMESPACE), revision, localName));
+ }
+
+ private static YangInstanceIdentifier.AugmentationIdentifier getAugmentIdentifier(final String... childNames) {
+ final Set<QName> qn = Sets.newHashSet();
+
+ for (final String childName : childNames) {
+ qn.add(getNodeIdentifier(childName).getNodeType());
+ }
+
+ return new YangInstanceIdentifier.AugmentationIdentifier(qn);
+ }
+
+ public NormalizedNodeXmlTranslationTest(final String yangPath, final String xmlPath,
+ final ContainerNode expectedNode) throws ReactorException {
+ this.schema = parseTestSchema(yangPath);
+ this.xmlPath = xmlPath;
+ this.containerNode = (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
+ this.expectedNode = expectedNode;
+ }
+
+ private final ContainerNode expectedNode;
+ private final ContainerSchemaNode containerNode;
+ private final String xmlPath;
+
+ private SchemaContext parseTestSchema(final String yangPath) throws ReactorException {
+ final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ reactor.addSource(new YangStatementSourceImpl(yangPath, false));
+ return reactor.buildEffective();
+ }
+
+ @Test
+ public void testTranslation() throws Exception {
+ final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream(xmlPath);
+
+ final XMLInputFactory factory = XMLInputFactory.newInstance();
+ final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream);
+
+ final NormalizedNodeResult result = new NormalizedNodeResult();
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+ final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schema);
+ xmlParser.parse(reader);
+
+ final NormalizedNode<?, ?> built = result.getResult();
+ assertNotNull(built);
+
+ if (expectedNode != null) {
+ org.junit.Assert.assertEquals(expectedNode, built);
+ }
+
+ final Document document = XmlDocumentUtils.getDocument();
+ final DOMResult domResult = new DOMResult(document);
+
+ final XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
+ outputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
+
+ final XMLStreamWriter xmlStreamWriter = outputFactory.createXMLStreamWriter(domResult);
+
+ final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter
+ .create(xmlStreamWriter, schema);
+
+ final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter
+ (xmlNormalizedNodeStreamWriter);
+
+ normalizedNodeWriter.write(built);
+
+ final Document doc = loadDocument(xmlPath);
+
+ XMLUnit.setIgnoreWhitespace(true);
+ XMLUnit.setIgnoreComments(true);
+ XMLUnit.setIgnoreAttributeOrder(true);
+ XMLUnit.setNormalize(true);
+
+ final String expectedXml = toString(doc.getDocumentElement().getElementsByTagName("container").item(0));
+ final String serializedXml = toString(domResult.getNode());
+
+ final Diff diff = new Diff(expectedXml, serializedXml);
+ diff.overrideDifferenceListener(new IgnoreTextAndAttributeValuesDifferenceListener());
+ diff.overrideElementQualifier(new ElementNameAndTextQualifier());
+
+ // FIXME the comparison cannot be performed, since the qualifiers supplied by XMlUnit do not work correctly in
+ // this case
+ // We need to implement custom qualifier so that the element ordering does not mess the DIFF
+ // dd.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(100, true));
+ // assertTrue(dd.toString(), dd.similar());
+
+ //new XMLTestCase() {}.assertXMLEqual(diff, true);
+ }
+
+ static final XMLOutputFactory XML_FACTORY;
+ static {
+ XML_FACTORY = XMLOutputFactory.newFactory();
+ XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
+ }
+
+ private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+ final InputStream resourceAsStream = NormalizedNodeXmlTranslationTest.class.getResourceAsStream(xmlPath);
+
+ final Document currentConfigElement = readXmlToDocument(resourceAsStream);
+ Preconditions.checkNotNull(currentConfigElement);
+ return currentConfigElement;
+ }
+
+ private static final DocumentBuilderFactory BUILDERFACTORY;
+
+ static {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setCoalescing(true);
+ factory.setIgnoringElementContentWhitespace(true);
+ factory.setIgnoringComments(true);
+ BUILDERFACTORY = factory;
+ }
+
+ private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+ final DocumentBuilder dBuilder;
+ try {
+ dBuilder = BUILDERFACTORY.newDocumentBuilder();
+ } catch (final ParserConfigurationException e) {
+ throw new RuntimeException("Failed to parse XML document", e);
+ }
+ final Document doc = dBuilder.parse(xmlContent);
+
+ doc.getDocumentElement().normalize();
+ return doc;
+ }
+
+ private static String toString(final Node xml) {
+ try {
+ final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ final StreamResult result = new StreamResult(new StringWriter());
+ final DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+
+ private static DataSchemaNode getSchemaNode(final SchemaContext context, final String moduleName,
+ final String childNodeName) {
+ for (Module module : context.getModules()) {
+ if (module.getName().equals(moduleName)) {
+ DataSchemaNode found = findChildNode(module.getChildNodes(), childNodeName);
+ Preconditions.checkState(found != null, "Unable to find %s", childNodeName);
+ return found;
+ }
+ }
+ throw new IllegalStateException("Unable to find child node " + childNodeName);
+ }
+
+ private static DataSchemaNode findChildNode(final Iterable<DataSchemaNode> children, final String name) {
+ List<DataNodeContainer> containers = Lists.newArrayList();
+
+ for (DataSchemaNode dataSchemaNode : children) {
+ if (dataSchemaNode.getQName().getLocalName().equals(name)) {
+ return dataSchemaNode;
+ }
+ if (dataSchemaNode instanceof DataNodeContainer) {
+ containers.add((DataNodeContainer) dataSchemaNode);
+ } else if (dataSchemaNode instanceof ChoiceSchemaNode) {
+ containers.addAll(((ChoiceSchemaNode) dataSchemaNode).getCases());
+ }
+ }
+
+ for (DataNodeContainer container : containers) {
+ DataSchemaNode retVal = findChildNode(container.getChildNodes(), name);
+ if (retVal != null) {
+ return retVal;
+ }
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+/*
+ * 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.codec.xml;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.DifferenceListener;
+import org.custommonkey.xmlunit.IgnoreTextAndAttributeValuesDifferenceListener;
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class NormalizedNodesToXmlTest {
+
+ private QNameModule bazModule;
+
+ private QName outerContainer;
+
+ private QName myContainer1;
+ private QName myKeyedList;
+ private QName myKeyLeaf;
+ private QName myLeafInList1;
+ private QName myLeafInList2;
+ private QName myLeaf1;
+ private QName myLeafList;
+
+ private QName myContainer2;
+ private QName innerContainer;
+ private QName myLeaf2;
+ private QName myLeaf3;
+ private QName myChoice;
+ private QName myLeafInCase2;
+
+ private QName myContainer3;
+ private QName myDoublyKeyedList;
+ private QName myFirstKeyLeaf;
+ private QName mySecondKeyLeaf;
+ private QName myLeafInList3;
+
+ @Before
+ public void setup() throws URISyntaxException, ParseException {
+ bazModule = QNameModule.create(new URI("baz-namespace"), SimpleDateFormatUtil.getRevisionFormat()
+ .parse("1970-01-01"));
+
+ outerContainer = QName.create(bazModule, "outer-container");
+
+ myContainer1 = QName.create(bazModule, "my-container-1");
+ myKeyedList = QName.create(bazModule, "my-keyed-list");
+ myKeyLeaf = QName.create(bazModule, "my-key-leaf");
+ myLeafInList1 = QName.create(bazModule, "my-leaf-in-list-1");
+ myLeafInList2 = QName.create(bazModule, "my-leaf-in-list-2");
+ myLeaf1 = QName.create(bazModule, "my-leaf-1");
+ myLeafList = QName.create(bazModule, "my-leaf-list");
+
+ myContainer2 = QName.create(bazModule, "my-container-2");
+ innerContainer = QName.create(bazModule, "inner-container");
+ myLeaf2 = QName.create(bazModule, "my-leaf-2");
+ myLeaf3 = QName.create(bazModule, "my-leaf-3");
+ myChoice = QName.create(bazModule, "my-choice");
+ myLeafInCase2 = QName.create(bazModule, "my-leaf-in-case-2");
+
+ myContainer3 = QName.create(bazModule, "my-container-3");
+ myDoublyKeyedList = QName.create(bazModule, "my-doubly-keyed-list");
+ myFirstKeyLeaf = QName.create(bazModule, "my-first-key-leaf");
+ mySecondKeyLeaf = QName.create(bazModule, "my-second-key-leaf");
+ myLeafInList3 = QName.create(bazModule, "my-leaf-in-list-3");
+ }
+
+ @Test
+ public void testNormalizedNodeToXmlSerialization() throws ReactorException, XMLStreamException, IOException,
+ SAXException {
+ final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ reactor.addSource(new YangStatementSourceImpl("/baz.yang", false));
+
+ final SchemaContext schemaContext = reactor.buildEffective();
+
+ final Document doc = loadDocument("/baz.xml");
+
+ final Document document = getDocument();
+ final DOMResult domResult = new DOMResult(document);
+
+ final XMLOutputFactory factory = XMLOutputFactory.newInstance();
+ factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
+
+ final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(domResult);
+
+ final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create
+ (xmlStreamWriter, schemaContext);
+
+ final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter
+ (xmlNormalizedNodeStreamWriter);
+
+ normalizedNodeWriter.write(buildOuterContainerNode());
+
+ XMLUnit.setIgnoreWhitespace(true);
+ XMLUnit.setNormalize(true);
+
+ final String expectedXml = toString(doc.getDocumentElement().getElementsByTagName("outer-container").item(0));
+ final String serializedXml = toString(domResult.getNode());
+ final Diff diff = new Diff(expectedXml, serializedXml);
+
+ final DifferenceListener differenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();
+ diff.overrideDifferenceListener(differenceListener);
+
+ new XMLTestCase() {}.assertXMLEqual(diff, true);
+ }
+
+ private NormalizedNode<?, ?> buildOuterContainerNode() {
+ // my-container-1
+ MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myKeyedList))
+ .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(myKeyedList, myKeyLeaf, "listkeyvalue1"))
+ .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1))
+ .withValue("listleafvalue1").build())
+ .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2))
+ .withValue("listleafvalue2").build()).build())
+ .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(myKeyedList, myKeyLeaf, "listkeyvalue2"))
+ .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1))
+ .withValue("listleafvalue12").build())
+ .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2))
+ .withValue("listleafvalue22").build()).build()).build();
+
+ LeafNode<?> myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf1))
+ .withValue("value1").build();
+
+ LeafSetNode<?> myLeafListNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(myLeafList))
+ .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+ new NodeWithValue<>(myLeafList, "lflvalue1")).withValue("lflvalue1").build())
+ .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+ new NodeWithValue<>(myLeafList, "lflvalue2")).withValue("lflvalue2").build()).build();
+
+ ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myContainer1))
+ .withChild(myKeyedListNode)
+ .withChild(myLeaf1Node)
+ .withChild(myLeafListNode).build();
+
+ // my-container-2
+ ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(innerContainer))
+ .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf2))
+ .withValue("value2").build()).build();
+
+ LeafNode<?> myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf3))
+ .withValue("value3").build();
+
+ ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(myChoice))
+ .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInCase2))
+ .withValue("case2value").build()).build();
+
+ ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myContainer2))
+ .withChild(innerContainerNode)
+ .withChild(myLeaf3Node)
+ .withChild(myChoiceNode).build();
+
+ // my-container-3
+ Map<QName, Object> keys = new HashMap<>();
+ keys.put(myFirstKeyLeaf, "listkeyvalue1");
+ keys.put(mySecondKeyLeaf, "listkeyvalue2");
+
+ MapNode myDoublyKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myDoublyKeyedList))
+ .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(myDoublyKeyedList, keys))
+ .withChild(Builders.leafBuilder().withNodeIdentifier(
+ new NodeIdentifier(myLeafInList3)).withValue("listleafvalue1").build()).build())
+ .build();
+
+ AugmentationNode myDoublyKeyedListAugNode = Builders.augmentationBuilder().withNodeIdentifier(
+ new AugmentationIdentifier(Sets.newHashSet(myDoublyKeyedList)))
+ .withChild(myDoublyKeyedListNode).build();
+
+ ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myContainer3))
+ .withChild(myDoublyKeyedListAugNode).build();
+
+ AugmentationNode myContainer3AugNode = Builders.augmentationBuilder().withNodeIdentifier(
+ new AugmentationIdentifier(Sets.newHashSet(myContainer3)))
+ .withChild(myContainer3Node).build();
+
+ ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(outerContainer))
+ .withChild(myContainer1Node)
+ .withChild(myContainer2Node)
+ .withChild(myContainer3AugNode).build();
+
+ return outerContainerNode;
+ }
+
+ private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+ final InputStream resourceAsStream = NormalizedNodesToXmlTest.class.getResourceAsStream(xmlPath);
+ final Document currentConfigElement = readXmlToDocument(resourceAsStream);
+ Preconditions.checkNotNull(currentConfigElement);
+ return currentConfigElement;
+ }
+
+ private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+ final DocumentBuilder dBuilder;
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setCoalescing(true);
+ factory.setIgnoringElementContentWhitespace(true);
+ factory.setIgnoringComments(true);
+ dBuilder = factory.newDocumentBuilder();
+ } catch (final ParserConfigurationException e) {
+ throw new RuntimeException("Failed to parse XML document", e);
+ }
+ final Document doc = dBuilder.parse(xmlContent);
+
+ doc.getDocumentElement().normalize();
+ return doc;
+ }
+
+ private static String toString(final Node xml) {
+ try {
+ final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ final StreamResult result = new StreamResult(new StringWriter());
+ final DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+
+ private static Document getDocument() {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ Document doc = null;
+ try {
+ DocumentBuilder bob = dbf.newDocumentBuilder();
+ doc = bob.newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ return doc;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.xml;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.net.URI;
+import java.util.Date;
+import java.util.List;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+public class RandomPrefixTest {
+ static final int MAX_COUNTER = 4000;
+
+ @Test
+ public void testEncodeDecode() throws Exception {
+
+ final List<String> allGenerated = Lists.newArrayList();
+ for (int i = 0; i < MAX_COUNTER; i++) {
+ final String encoded = RandomPrefix.encode(i);
+ assertEquals(RandomPrefix.decode(encoded), i);
+ allGenerated.add(encoded);
+ }
+
+ assertEquals(allGenerated.size(), MAX_COUNTER);
+ assertEquals("dPT", allGenerated.get(MAX_COUNTER - 1));
+ assertEquals("a", allGenerated.get(0));
+ assertEquals(allGenerated.size(), Sets.newHashSet(allGenerated).size());
+ }
+
+ @Test
+ public void testQNameWithPrefix() throws Exception {
+ final RandomPrefix a = new RandomPrefix();
+
+ final List<String> allGenerated = Lists.newArrayList();
+ for (int i = 0; i < MAX_COUNTER; i++) {
+ final String prefix = RandomPrefix.encode(i);
+ final URI uri = new URI("localhost:" + prefix);
+ final QName qName = QName.create(QNameModule.create(uri, new Date()), "local-name");
+ allGenerated.add(a.encodePrefix(qName.getNamespace()));
+ }
+
+ assertEquals(MAX_COUNTER, allGenerated.size());
+ // We are generating MAX_COUNTER_VALUE + 27 prefixes total, so we should encounter a reset in prefix a start from 0 at some point
+ // At the end, there should be only 27 values in RandomPrefix cache
+ assertEquals(MAX_COUNTER, Iterables.size(a.getPrefixes()));
+ assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xml")));
+ assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xmla")));
+ assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xmlz")));
+
+ assertEquals(1, Iterables.frequency(allGenerated, "a"));
+ }
+
+ @Test
+ public void test2QNames1Namespace() throws Exception {
+ final RandomPrefix a = new RandomPrefix();
+
+ final URI uri = URI.create("localhost");
+ final QName qName = QName.create(QNameModule.create(uri, new Date()), "local-name");
+ final QName qName2 = QName.create(QNameModule.create(uri, new Date()), "local-name");
+
+ assertEquals(a.encodePrefix(qName.getNamespace()), a.encodePrefix(qName2.getNamespace()));
+ }
+
+ @Test
+ public void testQNameNoPrefix() throws Exception {
+ final RandomPrefix a = new RandomPrefix();
+
+ final URI uri = URI.create("localhost");
+ QName qName = QName.create(uri, new Date(), "local-name");
+ assertEquals("a", a.encodePrefix(qName.getNamespace()));
+ qName = QName.create(QNameModule.create(uri, new Date()), "local-name");
+ assertEquals("a", a.encodePrefix(qName.getNamespace()));
+ qName = QName.create(QNameModule.create(URI.create("second"), new Date()), "local-name");
+ assertEquals("b", a.encodePrefix(qName.getNamespace()));
+
+ }
+}
--- /dev/null
+/*
+ * 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.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.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.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+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 CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ reactor.addSource(new YangStatementSourceImpl("/leafref-test.yang", false));
+
+ schemaContext = reactor.buildEffective();
+ 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")
+ */
+ // ignored because this isn't implemented
+ @Ignore
+ @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);
+ } else {
+ 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;
+ }
+}
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
--- /dev/null
+/*
+ * 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.codec.xml;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Collection;
+import java.util.List;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.YangModeledAnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+
+public class YangModeledAnyXMLDeserializationTest {
+
+ private QNameModule fooModuleQName;
+ private QNameModule barModuleQName;
+ private QName myContainer1;
+ private QName myContainer2;
+ private QName innerContainer;
+ private QName myLeaf3;
+ private QName myLeaf2;
+ private QName myLeaf1;
+ private QName myAnyXMLDataBar;
+ private QName myAnyXMLDataFoo;
+ private SchemaContext schemaContext;
+
+ @Before
+ public void Init() throws Exception {
+ barModuleQName = QNameModule.create(new URI("bar"), SimpleDateFormatUtil.getRevisionFormat()
+ .parse("1970-01-01"));
+ myContainer1 = QName.create(barModuleQName, "my-container-1");
+ myLeaf1 = QName.create(barModuleQName, "my-leaf-1");
+ myAnyXMLDataBar = QName.create(barModuleQName, "my-anyxml-data");
+
+ fooModuleQName = QNameModule.create(new URI("foo"), SimpleDateFormatUtil.getRevisionFormat()
+ .parse("1970-01-01"));
+ myContainer2 = QName.create(fooModuleQName, "my-container-2");
+ innerContainer = QName.create(fooModuleQName, "inner-container");
+ myLeaf3 = QName.create(fooModuleQName, "my-leaf-3");
+ myLeaf2 = QName.create(fooModuleQName, "my-leaf-2");
+ myAnyXMLDataFoo = QName.create(fooModuleQName, "my-anyxml-data");
+
+ CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ reactor.addSources(new YangStatementSourceImpl("/anyxml-support/yang/foo.yang", false),
+ new YangStatementSourceImpl("/anyxml-support/yang/bar.yang", false),
+ new YangStatementSourceImpl("/anyxml-support/yang/yang-ext.yang", false));
+
+ schemaContext = reactor.buildEffective();
+ }
+
+ @Test
+ public void testRawAnyXMLFromBar() throws Exception {
+ DataSchemaNode barContainer = schemaContext.getDataChildByName(QName.create(barModuleQName, "bar"));;
+ assertTrue(barContainer instanceof ContainerSchemaNode);
+ final YangModeledAnyXmlSchemaNode yangModeledAnyXML = new YangModeledAnyXMLSchemaNodeImplTest(myAnyXMLDataBar,
+ (ContainerSchemaNode) barContainer);
+
+ final InputStream resourceAsStream = YangModeledAnyXMLDeserializationTest.class.getResourceAsStream(
+ "/anyxml-support/xml/bar.xml");
+
+ final XMLInputFactory factory = XMLInputFactory.newInstance();
+ final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream);
+
+ final ImmutableContainerNodeBuilder result = (ImmutableContainerNodeBuilder) ImmutableContainerNodeSchemaAwareBuilder
+ .create(yangModeledAnyXML.getSchemaOfAnyXmlData());
+
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+ final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, yangModeledAnyXML);
+ xmlParser.parse(reader);
+
+ final NormalizedNode<?, ?> output = result.build();
+
+ Collection<DataContainerChild<? extends PathArgument, ?>> value = ((ContainerNode) output).getValue();
+ assertEquals(2, value.size());
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> child = ((ContainerNode) output)
+ .getChild(new NodeIdentifier(myContainer1));
+ assertTrue(child.orNull() instanceof ContainerNode);
+ ContainerNode myContainerNode1 = (ContainerNode) child.get();
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> child2 = myContainerNode1.getChild(new NodeIdentifier(
+ myLeaf1));
+ assertTrue(child2.orNull() instanceof LeafNode);
+ LeafNode<?> LeafNode1 = (LeafNode<?>) child2.get();
+
+ Object leafNode1Value = LeafNode1.getValue();
+ assertEquals("value1", leafNode1Value);
+ }
+
+ @Test
+ public void testRealSchemaContextFromFoo() throws Exception {
+ final InputStream resourceAsStream = YangModeledAnyXMLDeserializationTest.class.getResourceAsStream(
+ "/anyxml-support/xml/foo.xml");
+
+ final XMLInputFactory factory = XMLInputFactory.newInstance();
+ final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream);
+
+ final ImmutableContainerNodeBuilder result = (ImmutableContainerNodeBuilder) ImmutableContainerNodeSchemaAwareBuilder
+ .create(schemaContext);
+
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+ final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext);
+ xmlParser.parse(reader);
+
+ final NormalizedNode<?, ?> output = result.build();
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> child = ((ContainerNode) output).getChild(
+ new NodeIdentifier(myAnyXMLDataFoo));
+ assertTrue(child.orNull() instanceof YangModeledAnyXmlNode);
+ YangModeledAnyXmlNode yangModeledAnyXmlNode = (YangModeledAnyXmlNode) child.get();
+
+ DataSchemaNode schemaOfAnyXmlData = yangModeledAnyXmlNode.getSchemaOfAnyXmlData();
+ DataSchemaNode expectedSchemaOfAnyXmlData = schemaContext.getDataChildByName(myContainer2);
+ assertEquals(expectedSchemaOfAnyXmlData, schemaOfAnyXmlData);
+
+ Collection<DataContainerChild<? extends PathArgument, ?>> value = yangModeledAnyXmlNode.getValue();
+ assertEquals(2, value.size());
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> child2 = yangModeledAnyXmlNode
+ .getChild(new NodeIdentifier(innerContainer));
+ assertTrue(child2.orNull() instanceof ContainerNode);
+ ContainerNode innerContainerNode = (ContainerNode) child2.get();
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> child3 = innerContainerNode
+ .getChild(new NodeIdentifier(myLeaf2));
+ assertTrue(child3.orNull() instanceof LeafNode);
+ LeafNode<?> LeafNode2 = (LeafNode<?>) child3.get();
+
+ Object leafNode2Value = LeafNode2.getValue();
+ assertEquals("any-xml-leaf-2-value", leafNode2Value);
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> child4 = yangModeledAnyXmlNode
+ .getChild(new NodeIdentifier(myLeaf3));
+ assertTrue(child4.orNull() instanceof LeafNode);
+ LeafNode<?> LeafNode3 = (LeafNode<?>) child4.get();
+
+ Object leafNode3Value = LeafNode3.getValue();
+ assertEquals("any-xml-leaf-3-value", leafNode3Value);
+ }
+
+ private static class YangModeledAnyXMLSchemaNodeImplTest implements YangModeledAnyXmlSchemaNode {
+ private final QName qName;
+ private final ContainerSchemaNode contentSchema;
+
+ private YangModeledAnyXMLSchemaNodeImplTest(QName qName, ContainerSchemaNode contentSchema) {
+ this.qName = qName;
+ this.contentSchema = contentSchema;
+ }
+
+ @Override
+ public boolean isAugmenting() {
+ return false;
+ }
+
+ @Override
+ public boolean isAddedByUses() {
+ return false;
+ }
+
+ @Override
+ public boolean isConfiguration() {
+ return false;
+ }
+
+ @Override
+ public ConstraintDefinition getConstraints() {
+ return null;
+ }
+
+ @Override
+ public QName getQName() {
+ return qName;
+ }
+
+ @Override
+ public SchemaPath getPath() {
+ return null;
+ }
+
+ @Override
+ public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return null;
+ }
+
+ @Override
+ public String getReference() {
+ return null;
+ }
+
+ @Override
+ public Status getStatus() {
+ return null;
+ }
+
+ @Override
+ public ContainerSchemaNode getSchemaOfAnyXmlData() {
+ return contentSchema;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.codec.xml;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class YangModeledAnyXMLSerializationTest extends XMLTestCase {
+ private static final DocumentBuilderFactory BUILDERFACTORY;
+
+ static {
+ BUILDERFACTORY = DocumentBuilderFactory.newInstance();
+ BUILDERFACTORY.setNamespaceAware(true);
+ BUILDERFACTORY.setCoalescing(true);
+ BUILDERFACTORY.setIgnoringElementContentWhitespace(true);
+ BUILDERFACTORY.setIgnoringComments(true);
+ }
+
+ private QNameModule bazModuleQName;
+ private QName myAnyXMLDataBaz;
+ private QName bazQName;
+ private QName myContainer2QName;
+ private SchemaContext schemaContext;
+
+ public YangModeledAnyXMLSerializationTest() throws Exception {
+ bazModuleQName = QNameModule.create(new URI("baz"), SimpleDateFormatUtil.getRevisionFormat()
+ .parse("1970-01-01"));
+ bazQName = QName.create(bazModuleQName, "baz");
+ myContainer2QName = QName.create(bazModuleQName, "my-container-2");
+ myAnyXMLDataBaz = QName.create(bazModuleQName, "my-anyxml-data");
+
+ CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ reactor.addSources(new YangStatementSourceImpl("/anyxml-support/serialization/baz.yang", false),
+ new YangStatementSourceImpl("/anyxml-support/serialization/yang-ext.yang", false));
+ schemaContext = reactor.buildEffective();
+ }
+
+ @Test
+ public void testSerializationOfBaz() throws Exception {
+ final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/anyxml-support/serialization/baz.xml");
+
+ final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
+ final XMLStreamReader reader = inputFactory.createXMLStreamReader(resourceAsStream);
+
+ final NormalizedNodeResult result = new NormalizedNodeResult();
+
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+ final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext);
+ xmlParser.parse(reader);
+
+ final NormalizedNode<?, ?> transformedInput = result.getResult();
+ assertNotNull(transformedInput);
+
+ assertTrue(transformedInput instanceof ContainerNode);
+ ContainerNode bazContainer = (ContainerNode) transformedInput;
+ assertEquals(bazContainer.getNodeType(), bazQName);
+
+ Optional<DataContainerChild<? extends PathArgument, ?>> bazContainerChild = bazContainer.getChild(
+ new NodeIdentifier(myAnyXMLDataBaz));
+ assertTrue(bazContainerChild.orNull() instanceof YangModeledAnyXmlNode);
+ YangModeledAnyXmlNode yangModeledAnyXmlNode = (YangModeledAnyXmlNode) bazContainerChild.get();
+
+ DataSchemaNode schemaOfAnyXmlData = yangModeledAnyXmlNode.getSchemaOfAnyXmlData();
+ SchemaNode myContainer2SchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext,
+ SchemaPath.create(true, bazQName, myContainer2QName));
+ assertTrue(myContainer2SchemaNode instanceof ContainerSchemaNode);
+ assertEquals(myContainer2SchemaNode, schemaOfAnyXmlData);
+
+ final Document document = getDocument();
+ final DOMResult domResult = new DOMResult(document);
+
+ final XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
+ outputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
+
+ final XMLStreamWriter xmlStreamWriter = outputFactory.createXMLStreamWriter(domResult);
+
+ final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter
+ .create(xmlStreamWriter, schemaContext);
+
+ final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter
+ (xmlNormalizedNodeStreamWriter);
+
+ normalizedNodeWriter.write(transformedInput);
+
+ final Document doc = loadDocument("/anyxml-support/serialization/baz.xml");
+
+ XMLUnit.setIgnoreWhitespace(true);
+ XMLUnit.setIgnoreComments(true);
+ XMLUnit.setIgnoreAttributeOrder(true);
+ XMLUnit.setNormalize(true);
+
+ String expectedXml = toString(doc.getDocumentElement().getElementsByTagName("baz").item(0));
+ String serializedXml = toString(domResult.getNode());
+
+ assertXMLEqual(expectedXml, serializedXml);
+ }
+
+ private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+ final InputStream resourceAsStream = YangModeledAnyXMLSerializationTest.class.getResourceAsStream(xmlPath);
+ final Document currentConfigElement = readXmlToDocument(resourceAsStream);
+ Preconditions.checkNotNull(currentConfigElement);
+ return currentConfigElement;
+ }
+
+ private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+ final DocumentBuilder dBuilder;
+ try {
+ dBuilder = BUILDERFACTORY.newDocumentBuilder();
+ } catch (final ParserConfigurationException e) {
+ throw new RuntimeException("Failed to parse XML document", e);
+ }
+ final Document doc = dBuilder.parse(xmlContent);
+
+ doc.getDocumentElement().normalize();
+ return doc;
+ }
+
+ private static String toString(final Node xml) {
+ try {
+ final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ final StreamResult result = new StreamResult(new StringWriter());
+ final DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+
+ private static Document getDocument() {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ Document doc = null;
+ try {
+ DocumentBuilder bob = dbf.newDocumentBuilder();
+ doc = bob.newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ return doc;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="baz">
+ <baz>
+ <my-container-1>
+ <my-leaf-1>value1</my-leaf-1>
+ </my-container-1>
+ <my-container-2>
+ <inner-container>
+ <my-leaf-2>value2</my-leaf-2>
+ </inner-container>
+ <my-leaf-3>value3</my-leaf-3>
+ </my-container-2>
+
+ <my-anyxml-data>
+ <inner-container>
+ <my-leaf-2>any-xml-leaf-2-value</my-leaf-2>
+ </inner-container>
+ <my-leaf-3>any-xml-leaf-3-value</my-leaf-3>
+ </my-anyxml-data>
+ </baz>
+</root>
\ No newline at end of file
--- /dev/null
+module baz {
+ namespace "baz";
+ prefix baz;
+
+ import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+ container baz {
+ container my-container-1 {
+ leaf my-leaf-1 {
+ type string;
+ }
+ }
+
+ container my-container-2 {
+ container inner-container {
+ leaf my-leaf-2 {
+ type string;
+ }
+ }
+ leaf my-leaf-3 {
+ type string;
+ }
+ }
+
+ anyxml my-anyxml-data {
+ ext:anyxml-schema-location "/baz/my-container-2";
+ }
+ }
+}
--- /dev/null
+module yang-ext {
+ yang-version 1;
+ namespace "urn:opendaylight:yang:extension:yang-ext";
+ prefix "ext";
+
+ contact "Anton Tkacik <ttkacik@cisco.com>";
+
+ description
+ "Copyright (c) 2013 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";
+
+ revision "2013-07-09" {
+ description "";
+ }
+
+ // Augmentation name
+
+ extension "augment-identifier" {
+ description
+ "YANG language extension which assigns an identifier to augmentation.
+ Augment identifier is used to identify specific augment statement
+ by name. The identifier syntax is defined formally defined by
+ the rule 'identifier' in Section 12 of RFC 6020. All augment identifiers
+ defined in a namespace MUST be unique. The namespace of augment
+ identifiers is shared by module and its submodules.";
+
+ /*
+ Discussion:
+ This extension allows for ease of development / debug
+ of YANG modules and it is suitable for code generation,
+ where each augment statement is nicely identified by
+ unique name instead of combination of augment target
+ and when condition.
+ */
+ argument "identifier";
+ }
+
+ // Context-aware RPCs
+
+ grouping rpc-context-ref {
+ description
+ "A reference to RPC context.";
+ leaf context-instance {
+ type instance-identifier;
+ description "Pointer to the context. ";
+ }
+ }
+
+ extension "rpc-context-instance" {
+ description
+ "YANG language extension which defines enclosing (parent) schema
+ node as referencable context for RPCs. The argument is identity
+ which is used to identify RPC context type.";
+
+ argument "context-type";
+ }
+
+ extension "context-reference" {
+ argument "context-type";
+ }
+
+ extension "context-instance" {
+ argument "context-type";
+ }
+
+ extension "instance-target" {
+ argument "path";
+ }
+
+ extension "anyxml-schema-location" {
+ argument "target-node";
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<root xmlns="bar">
+ <my-container-1>
+ <my-leaf-1>value1</my-leaf-1>
+ </my-container-1>
+ <my-container-2>
+ <my-leaf-2>value2</my-leaf-2>
+ </my-container-2>
+</root>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+ <my-container-1>
+ <my-leaf-1>value1</my-leaf-1>
+ </my-container-1>
+ <my-container-2>
+ <inner-container>
+ <my-leaf-2>value2</my-leaf-2>
+ </inner-container>
+ <my-leaf-3>value3</my-leaf-3>
+ </my-container-2>
+
+ <my-anyxml-data>
+ <inner-container>
+ <my-leaf-2>any-xml-leaf-2-value</my-leaf-2>
+ </inner-container>
+ <my-leaf-3>any-xml-leaf-3-value</my-leaf-3>
+ </my-anyxml-data>
+</foo>
\ No newline at end of file
--- /dev/null
+module bar {
+ namespace "bar";
+ prefix bar;
+
+ container bar {
+ container my-container-1 {
+ leaf my-leaf-1 {
+ type string;
+ }
+ }
+
+ container my-container-2 {
+ leaf my-leaf-2 {
+ type string;
+ }
+ }
+ }
+}
--- /dev/null
+module foo {
+ namespace "foo";
+ prefix foo;
+
+ import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+ container my-container-1 {
+ leaf my-leaf-1 {
+ type string;
+ }
+ }
+
+ container my-container-2 {
+ container inner-container {
+ leaf my-leaf-2 {
+ type string;
+ }
+ }
+ leaf my-leaf-3 {
+ type string;
+ }
+ }
+
+ anyxml my-anyxml-data {
+ ext:anyxml-schema-location "/my-container-2";
+ }
+}
--- /dev/null
+module yang-ext {
+ yang-version 1;
+ namespace "urn:opendaylight:yang:extension:yang-ext";
+ prefix "ext";
+
+ contact "Anton Tkacik <ttkacik@cisco.com>";
+
+ description
+ "Copyright (c) 2013 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";
+
+ revision "2013-07-09" {
+ description "";
+ }
+
+ // Augmentation name
+
+ extension "augment-identifier" {
+ description
+ "YANG language extension which assigns an identifier to augmentation.
+ Augment identifier is used to identify specific augment statement
+ by name. The identifier syntax is defined formally defined by
+ the rule 'identifier' in Section 12 of RFC 6020. All augment identifiers
+ defined in a namespace MUST be unique. The namespace of augment
+ identifiers is shared by module and its submodules.";
+
+ /*
+ Discussion:
+ This extension allows for ease of development / debug
+ of YANG modules and it is suitable for code generation,
+ where each augment statement is nicely identified by
+ unique name instead of combination of augment target
+ and when condition.
+ */
+ argument "identifier";
+ }
+
+ // Context-aware RPCs
+
+ grouping rpc-context-ref {
+ description
+ "A reference to RPC context.";
+ leaf context-instance {
+ type instance-identifier;
+ description "Pointer to the context. ";
+ }
+ }
+
+ extension "rpc-context-instance" {
+ description
+ "YANG language extension which defines enclosing (parent) schema
+ node as referencable context for RPCs. The argument is identity
+ which is used to identify RPC context type.";
+
+ argument "context-type";
+ }
+
+ extension "context-reference" {
+ argument "context-type";
+ }
+
+ extension "context-instance" {
+ argument "context-type";
+ }
+
+ extension "instance-target" {
+ argument "path";
+ }
+
+ extension "anyxml-schema-location" {
+ argument "target-node";
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+ <root>
+ <my-leaf>dp1o34</my-leaf>
+ </root>
+</foo>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+ <root>
+ <my-leaf>dp0s3f9</my-leaf>
+ </root>
+</foo>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+ <root>
+ <my-leaf>dp09P1p2s3</my-leaf>
+ </root>
+</foo>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+ <root>
+ <my-leaf>dp0p3p1</my-leaf>
+ </root>
+</foo>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+ <root>
+ <my-leaf>dp0s3</my-leaf>
+ </root>
+</foo>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+ <root>
+ <my-leaf>dp09P1p2s1234</my-leaf>
+ </root>
+</foo>
\ No newline at end of file
--- /dev/null
+module foo {
+ yang-version 1;
+ namespace "foo";
+ prefix "foo";
+
+ revision "2016-03-22" {
+ description "test";
+ }
+
+ container root {
+ leaf my-leaf {
+ type my-type;
+ }
+ }
+
+ typedef my-type {
+ type union {
+ type string {
+ pattern "dp[0-9]+o[0-9]+";
+ }
+ type string {
+ pattern "dp[0-9]+s[0-9]+(f[0-9]+)?(d[0-9]+)?";
+ }
+ type string {
+ pattern "dp[0-9]+(P[0-9]+)?p[0-9]{1,3}s[0-9]{1,3}(f[0-9]+)?(d[0-9]+)?";
+ }
+ type string {
+ pattern "dp[0-9]+p[0-9]+p[0-9]+";
+ }
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+ <root>
+ <ip-address>fwAAAQ==</ip-address>
+ </root>
+</foo>
\ No newline at end of file
--- /dev/null
+module foo {
+ yang-version 1;
+ namespace "foo";
+ prefix "foo";
+
+ revision "2015-11-05" {
+ }
+
+ typedef ipv4-address-binary {
+ type binary {
+ length "4";
+ }
+ }
+
+ typedef ipv6-address-binary {
+ type binary {
+ length "16";
+ }
+ }
+
+ typedef ip-address-binary {
+ type union {
+ type ipv4-address-binary;
+ type ipv6-address-binary;
+ }
+ }
+
+ container root {
+ leaf ip-address {
+ type ip-address-binary;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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
+ */
+module leafref-test {
+ yang-version 1;
+ namespace "urn:opendaylight:yangtools:leafref:test";
+ prefix "lt";
+
+ revision 2014-11-04 {
+ description "Test deserialization value of leafref type.";
+ }
+
+ identity test-identity-base {
+ }
+
+ identity test-identity {
+ base lt:test-identity-base;
+ }
+
+ container interface {
+ leaf simpleValue {
+ type instance-identifier;
+ }
+ }
+
+ container cont2 {
+ container cont3 {
+ leaf leafname3 {
+ type leafref {
+ path "../../pointToStringLeaf";
+ }
+ }
+ }
+ leaf pointToStringLeaf {
+ type leafref {
+ path "../stringleaf";
+ }
+ }
+
+ leaf point-to-identityrefleaf {
+ type leafref {
+ path "../identityrefleaf";
+ }
+ }
+
+ leaf identityrefleaf {
+ type identityref {
+ base lt:test-identity-base;
+ }
+ }
+
+ leaf stringleaf {
+ type string;
+ }
+ leaf absname {
+ type leafref {
+ path "/lt:interface/lt:simpleValue";
+ }
+ }
+ leaf relname {
+ type leafref {
+ path "../../lt:interface/lt:simpleValue";
+ }
+ }
+
+ leaf lf-with-double-point-inside {
+ type leafref {
+ path "../../lt:interface/../lt:cont2/lt:stringleaf";
+ }
+ }
+ }
+}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:test";
+ prefix "test";
+
+ organization "Cisco Systems, Inc.";
+
+ revision "2014-3-13" {
+ description
+ "Initial revision";
+ }
+
+
+ container container {
+ choice ch2{}
+ choice ch3{
+ case c3 {
+ leaf c3Leaf {
+ type string;
+ }
+ }
+ }
+ }
+
+ augment "/container/" {
+ leaf augLeaf {
+ type string;
+ }
+ }
+
+ augment "/container/" {
+ choice ch{}
+ }
+
+ augment "/container/ch/" {
+ case c1 {
+ leaf c1Leaf {
+ type string;
+ }
+ }
+
+ leaf c12 {
+ type string;
+ }
+ }
+ augment "/container/ch/c1/" {
+ leaf c1Leaf_AnotherAugment {
+ type string;
+ }
+
+ choice deepChoice{}
+ }
+
+ augment "/container/ch3/" {
+ case c32 {
+ leaf c32Leaf {
+ type string;
+ }
+ }
+
+ leaf c34LeafS {
+ type string;
+ }
+ }
+
+
+ augment "/container/ch/c1/deepChoice/" {
+ case deepCase1 {
+ leaf deepLeafc1 {
+ type string;
+ }
+ }
+ case deepCase2 {
+ leaf deepLeafc2 {
+ type string;
+ }
+ }
+ }
+
+ augment "/container/ch2/" {
+ case c2 {
+ leaf c2Leaf {
+ type string;
+ }
+
+ choice c2DeepChoice {
+ case c2DeepChoiceCase1 {
+ leaf c2DeepChoiceCase1Leaf1 {
+ type string;
+ }
+ }
+ case c2DeepChoiceCase2 {
+ leaf c2DeepChoiceCase1Leaf2 {
+ type string;
+ }
+ }
+ }
+ }
+ }
+
+ augment "/container/ch2/" {
+ leaf c22Leaf {
+ type string;
+ }
+ }
+
+ augment "/container" {
+ /*ext:augment-identifier top-choice-augment1;*/
+ choice augment-choice1 {
+ case case1 {
+ container case1-container {
+ leaf case1-leaf {
+ type string;
+ }
+ }
+ }
+
+ case case2 {
+ container case2-container {
+ leaf case2-leaf {
+ type string;
+ }
+ }
+ }
+ }
+ }
+
+ augment "/container/augment-choice1/case1" {
+ /*ext:augment-identifier top-choice-augment2;*/
+ choice augment-choice2 {
+ case case11 {
+ container case11-choice-case-container {
+ leaf case11-choice-case-leaf {
+ type string;
+ }
+ }
+ }
+ }
+ }
+
+
+}
--- /dev/null
+<root xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <container>
+ <c2Leaf>2</c2Leaf>
+ <c2DeepChoiceCase1Leaf2>2</c2DeepChoiceCase1Leaf2>
+ <!--<c2DeepChoiceCase1Leaf1>2</c2DeepChoiceCase1Leaf1>-->
+
+ <c3Leaf>3</c3Leaf>
+
+ <augLeaf>augment</augLeaf>
+
+ <c1Leaf>1</c1Leaf>
+ <c1Leaf_AnotherAugment>1</c1Leaf_AnotherAugment>
+ <deepLeafc1>1</deepLeafc1>
+ <!--<deepLeafc2>1</deepLeafc2>-->
+ </container>
+</root>
+
--- /dev/null
+<root xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <container>
+ <c22Leaf>2</c22Leaf>
+
+ <c34LeafS>3</c34LeafS>
+
+ <augLeaf>augment</augLeaf>
+
+ <c1Leaf>1</c1Leaf>
+ <c1Leaf_AnotherAugment>1</c1Leaf_AnotherAugment>
+ <deepLeafc2>1</deepLeafc2>
+ </container>
+</root>
+
--- /dev/null
+<root xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <container>
+ <case11-choice-case-container>
+ <case11-choice-case-leaf>leaf-value</case11-choice-case-leaf>
+ </case11-choice-case-container>
+ </container>
+</root>
+
--- /dev/null
+module rpc-test-model {
+yang-version 1;
+ namespace "org:opendaylight:rpc-reply:test:ns:yang";
+ prefix "user";
+
+ organization "Cisco Systems";
+ contact "Lukas Sedlak";
+ description "Test model for testing rpc input message translation to DOM Nodes.";
+
+ revision "2014-07-17" {
+ description "Initial revision";
+ }
+
+ rpc rock-the-house {
+ output {
+ leaf zip-code {
+ type string;
+ }
+ }
+ }
+
+ rpc activate-software-image {
+ input {
+ leaf image-name {
+ type string;
+ }
+ }
+ output {
+ container image-properties {
+ list image-property {
+ key "image-id";
+
+ leaf image-id {
+ type string;
+ }
+ }
+ }
+
+ leaf status {
+ type string;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <trpc:zip-code xmlns:trpc="org:opendaylight:rpc-reply:test:ns:yang">123014</trpc:zip-code>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
+ xmlns:rtm="org:opendaylight:rpc-reply:test:ns:yang"
+ rtm:image-name="img.img">
+
+ <image-properties xmlns="org:opendaylight:rpc-reply:test:ns:yang">
+ <image-property>
+ <image-id>id12345_test_data</image-id>
+ </image-property>
+ </image-properties>
+ <status xmlns="org:opendaylight:rpc-reply:test:ns:yang">testing_data</status>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<root>
+ <container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <boolean>true</boolean>
+ <innerContainer>
+ <uint16>44</uint16>
+ </innerContainer>
+ <leafList>a</leafList>
+ <leafList>b</leafList>
+
+ <list>
+ <uint32InList>1</uint32InList>
+ <containerInList name="inContainer">
+ <uint32>32</uint32>
+ <uint16>16</uint16>
+ <identityr xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:b</identityr>
+ <anyX xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">direct Value</anyX>
+ <uint16-ref>16</uint16-ref>
+ <instance-id xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">/prefix:container/prefix:list[prefix:uint32InList="1"]</instance-id>
+ </containerInList>
+ <stringAugmentedToList>augmentInList</stringAugmentedToList>
+ <stringAugmentedToListInCase1>augmentInListCase1</stringAugmentedToListInCase1>
+ </list>
+ <list>
+ <uint32InList>2</uint32InList>
+ <containerInList>
+ <uint32>32</uint32>
+ <uint16>16</uint16>
+ <identityr>b</identityr>
+ <anyX>
+ <container xmlns="randomNamespace" xmlns:pref="prefixed:namespace">
+ <pref:boolean>true</pref:boolean>
+ <innerContainer xmlns:p="prefixed:namespace">
+ <uint16 xmlns:pref2="prefixed:namespace">pref2:44</uint16>
+ </innerContainer>
+ </container>
+ <container2 xmlns="randomNamespace2"/>
+ </anyX>
+ </containerInList>
+ <stringAugmentedToListInCase2>augmentInListCase2</stringAugmentedToListInCase2>
+ </list>
+ <list>
+ <uint32InList>4</uint32InList>
+ <containerInList>
+ <anyX>
+ <netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
+ <schemas>
+ <schema>
+ <identifier>module</identifier>
+ <version>2012-12-12</version>
+ <format xmlns:x="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">x:yang</format>
+ </schema>
+ </schemas>
+ </netconf-state>
+ </anyX>
+ </containerInList>
+ </list>
+ <list>
+ <uint32InList>3</uint32InList>
+ </list>
+
+ <augmentString1>choice1Case1</augmentString1>
+ <augmentInt1>41</augmentInt1>
+ <stringInAugmentedCaseInAugmentedChoiceFromAugment>deep</stringInAugmentedCaseInAugmentedChoiceFromAugment>
+
+ <augmentContainer>
+ <augmentStringInaugmentContainer>choice2Case1</augmentStringInaugmentContainer>
+ </augmentContainer>
+
+ <augmentUint32>999</augmentUint32>
+
+
+
+ </container>
+</root>
\ No newline at end of file
--- /dev/null
+<root>
+ <container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <augmentString1>choice1Case1</augmentString1>
+ <augmentInt1>41</augmentInt1>
+ <stringInAugmentedCaseInAugmentedChoice2>deep</stringInAugmentedCaseInAugmentedChoice2>
+
+ <augmentedList>
+ <augmentStringInaugmentList>aug1</augmentStringInaugmentList>
+ </augmentedList>
+
+ <augmentedList>
+ <augmentStringInaugmentList>aug2</augmentStringInaugmentList>
+ <augmentedContainerInAugmentedListInAugmentedCase>
+ <leafInAugmentedContainerInAugmentedListInAugmentedCase>
+ 66
+ </leafInAugmentedContainerInAugmentedListInAugmentedCase>
+ </augmentedContainerInAugmentedListInAugmentedCase>
+
+ <augmentedListInAugmentedListInAugmentedCase>
+ <leafInAugmentedListInAugmentedListInAugmentedCase>
+ 661
+ </leafInAugmentedListInAugmentedListInAugmentedCase>
+ <leafInAugmentedListInAugmentedListInAugmentedCase>
+ 662
+ </leafInAugmentedListInAugmentedListInAugmentedCase>
+ </augmentedListInAugmentedListInAugmentedCase>
+
+ <augmentedListInAugmentedListInAugmentedCase>
+ <leafInAugmentedListInAugmentedListInAugmentedCase>
+ 6621
+ </leafInAugmentedListInAugmentedListInAugmentedCase>
+ </augmentedListInAugmentedListInAugmentedCase>
+ </augmentedList>
+
+ <augmentedList>
+ <augmentStringInaugmentList>aug3</augmentStringInaugmentList>
+ <augmentedContainerInAugmentedListInAugmentedCase/>
+
+ <augmentedListInAugmentedListInAugmentedCase/>
+ </augmentedList>
+
+ </container>
+</root>
+
--- /dev/null
+<root>
+ <container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test" name="test" xmlns:foo="http://www.foo.com/" foo:baz="baz">
+
+ <list list="on list entry">
+ <uint32InList name="test" foo:baz="baz">3</uint32InList>
+ </list>
+
+ <boolean xmlns:leaf="test:namespace:in:leaf" leaf:a="b">false</boolean>
+
+ <leafList foo:b="b">a</leafList>
+
+ </container>
+</root>
+
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:test";
+ prefix "test";
+
+ organization "Cisco Systems, Inc.";
+
+ revision "2014-3-13" {
+ description
+ "Initial revision";
+ }
+
+ identity a {}
+
+ identity b {
+ base "test:a";
+ }
+
+ grouping listGroup {
+ list list {
+ key "uint32InList";
+
+ leaf uint32InList {
+ type uint32;
+ }
+
+ container containerInList{
+ leaf uint32 {
+ type uint32;
+ }
+ leaf uint16 {
+ type uint16;
+ }
+
+ leaf identityr {
+ type identityref {
+ base "test:a";
+ }
+ }
+
+ leaf uint16-ref {
+ type leafref {
+ path "../uint16";
+ }
+ }
+
+ leaf instance-id {
+ type instance-identifier;
+ }
+
+ anyxml anyX;
+ }
+ }
+ }
+
+ grouping innerContainerGrouping {
+ container innerContainer {
+ leaf uint16 {
+ type uint16;
+ }
+
+ container innerInnerContainer {
+
+ leaf uint16 {
+ type uint16;
+ }
+
+ leaf uint32 {
+ type uint32;
+ }
+ }
+ }
+ }
+
+ container container {
+ leaf uint32 {
+ type uint32;
+ }
+
+ leaf decimal64 {
+ type decimal64 {
+ fraction-digits 2;
+ }
+ }
+
+ leaf boolean {
+ type boolean;
+ }
+
+ leaf binary {
+ type binary;
+ }
+
+ leaf string {
+ type string;
+ }
+
+ uses listGroup {
+ augment "list/" {
+ leaf stringAugmentedToList{
+ type string;
+ }
+
+ choice choiceInList {
+ case caseInList1 {
+ leaf stringAugmentedToListInCase1 {
+ type string;
+ }
+ }
+ case caseInList2 {
+ leaf stringAugmentedToListInCase2 {
+ type string;
+ }
+ }
+ }
+ }
+ }
+
+ list directList {
+ leaf stringInDirectList {
+ type string;
+ }
+ }
+
+ uses innerContainerGrouping;
+
+ choice choice{}
+ choice choice2{}
+
+ leaf-list leafList {
+ type string;
+ }
+
+ leaf identityRef {
+ type identityref {
+ base test-identity;
+ }
+ }
+ }
+
+ augment "/container/" {
+ leaf augmentUint32 {
+ type uint32;
+ }
+ }
+
+ augment "/container/directList/" {
+ leaf augmentedString {
+ type uint32;
+ }
+ }
+
+ augment "/container/choice/" {
+ case test-identity-augment {
+ when "/container/identityRef = 'test-identity'";
+ leaf augmentString1 {
+ type string;
+ }
+
+ leaf augmentInt1 {
+ type uint32;
+ }
+ }
+ case test-identity-augment2 {
+ when "/container/identityRef = 'test-identity2'";
+ leaf augmentString2 {
+ type string;
+ }
+
+ leaf augmentInt2 {
+ type uint32;
+ }
+ }
+ }
+
+ augment "/container/choice/test-identity-augment/" {
+
+ choice augmentedChoiceInCase {
+
+ case augmentedCaseInAugmentedChoice {
+ leaf stringInAugmentedCaseInAugmentedChoice {
+ type string;
+ }
+ }
+
+ case augmentedCaseInAugmentedChoice2 {
+ leaf stringInAugmentedCaseInAugmentedChoice2 {
+ type string;
+ }
+ }
+ }
+ }
+
+ augment "/container/choice/test-identity-augment/augmentedChoiceInCase/" {
+ case augmentedCaseInAugmentedChoiceFromAugment {
+ leaf stringInAugmentedCaseInAugmentedChoiceFromAugment {
+ type string;
+ }
+ }
+ }
+
+ augment "/container/choice2/" {
+ case test-identity-augment {
+ when "/container/identityRef = 'test-identity'";
+ container augmentContainer {
+ leaf augmentStringInaugmentContainer {
+ type string;
+ }
+ }
+ }
+ case test-identity-augment2 {
+ when "/container/identityRef = 'test-identity2'";
+ list augmentedList {
+ leaf augmentStringInaugmentList {
+ type string;
+ }
+ }
+ }
+ }
+
+
+ augment "/container/choice2/test-identity-augment2/augmentedList/" {
+
+ container augmentedContainerInAugmentedListInAugmentedCase {
+ leaf-list leafInAugmentedContainerInAugmentedListInAugmentedCase {
+ type uint32;
+ }
+ }
+
+ list augmentedListInAugmentedListInAugmentedCase {
+ leaf-list leafInAugmentedListInAugmentedListInAugmentedCase {
+ type uint32;
+ }
+ }
+ }
+
+ identity test-identity {}
+ identity test-identity2 {
+ base test-identity;
+ }
+
+}
\ No newline at end of file