Bug 3874: Support of yang modeled AnyXML - serialization XML/JSON 41/29741/15
authorPeter Kajsa <pkajsa@cisco.com>
Mon, 16 Nov 2015 10:00:47 +0000 (11:00 +0100)
committerRobert Varga <nite@hq.sk>
Sat, 9 Jan 2016 13:44:12 +0000 (13:44 +0000)
- added support of yang modeled anyXml serialization to XML and JSON
- basic unit tests

Change-Id: I60ed3782cd9852c8e2be69c8b8abf825738d16e6
Signed-off-by: Peter Kajsa <pkajsa@cisco.com>
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/YangModeledAnyXmlSupportTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/json/baz.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/xml/baz.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/baz.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/yang-ext.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/retest/YangModeledAnyXMLSerializationTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/serialization/yang-ext.yang [new file with mode: 0644]

index 1c7147f3c02ca8c8d752a0299dfd185d79d1fdad..b05b547dde49cc97084c777eef8dda8634885657 100644 (file)
@@ -35,6 +35,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -93,7 +94,7 @@ public class NormalizedNodeWriter implements Closeable, Flushable {
      * events to the encapsulated {@link NormalizedNodeStreamWriter}.
      *
      * @param node Node
-     * @return
+     * @return NormalizedNodeWriter this
      * @throws IOException when thrown from the backing writer.
      */
     public final NormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
@@ -174,7 +175,7 @@ public class NormalizedNodeWriter implements Closeable, Flushable {
     }
 
     protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
-        if(writer instanceof NormalizedNodeStreamAttributeWriter) {
+        if (writer instanceof NormalizedNodeStreamAttributeWriter) {
             ((NormalizedNodeStreamAttributeWriter) writer)
                     .startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
         } else {
@@ -193,6 +194,15 @@ public class NormalizedNodeWriter implements Closeable, Flushable {
             }
             return writeChildren(n.getValue());
         }
+        if (node instanceof YangModeledAnyXmlNode) {
+            final YangModeledAnyXmlNode n = (YangModeledAnyXmlNode) node;
+            if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+                ((NormalizedNodeStreamAttributeWriter) writer).startYangModeledAnyXmlNode(n.getIdentifier(), childSizeHint(n.getValue()), n.getAttributes());
+            } else {
+                writer.startYangModeledAnyXmlNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            }
+            return writeChildren(n.getValue());
+        }
         if (node instanceof MapEntryNode) {
             return writeMapEntryNode((MapEntryNode) node);
         }
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/YangModeledAnyXmlSupportTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/YangModeledAnyXmlSupportTest.java
new file mode 100644 (file)
index 0000000..0b36891
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.gson.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.loadModules;
+
+import com.google.common.base.Preconditions;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+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 org.junit.BeforeClass;
+import org.junit.Test;
+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.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
+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 {
+        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);
+    }
+
+    @Test
+    public void normalizedNodesToJsonTest() throws IOException, URISyntaxException, SAXException {
+        final DataContainerChild<? extends PathArgument, ?> baz = data.getValue().iterator().next();
+
+        final Writer writer = new StringWriter();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, baz);
+
+        final JsonParser parser = new JsonParser();
+        final JsonElement serializedJson = parser.parse(jsonOutput);
+        final JsonElement expextedJson = parser.parse(new FileReader(new File(getClass().getResource(
+                "/yang-modeled-anyxml/json/baz.json").toURI())));
+
+        assertEquals(expextedJson, serializedJson);
+    }
+
+    private String normalizedNodeToJsonStreamTransformation(final Writer writer,
+            final NormalizedNode<?, ?> inputStructure) throws IOException {
+
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+                JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, null,
+                JsonWriterFactory.createJsonWriter(writer, 2));
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        nodeWriter.write(inputStructure);
+
+        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;
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/json/baz.json b/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/json/baz.json
new file mode 100644 (file)
index 0000000..11c65c2
--- /dev/null
@@ -0,0 +1,19 @@
+{
+   "baz:baz":{
+      "my-container-1":{
+         "my-leaf-1":"value1"
+      },
+      "my-container-2":{
+         "inner-container":{
+            "my-leaf-2":"value2"
+         },
+         "my-leaf-3":"value3"
+      },
+      "my-anyxml-data":{
+         "inner-container":{
+            "my-leaf-2":"any-xml-leaf-2-value"
+         },
+         "my-leaf-3":"any-xml-leaf-3-value"
+      }
+   }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/xml/baz.xml b/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/xml/baz.xml
new file mode 100644 (file)
index 0000000..16029d1
--- /dev/null
@@ -0,0 +1,21 @@
+<?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
diff --git a/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/baz.yang b/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/baz.yang
new file mode 100644 (file)
index 0000000..9cd2233
--- /dev/null
@@ -0,0 +1,29 @@
+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";
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/yang-ext.yang b/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/yang-ext.yang
new file mode 100644 (file)
index 0000000..2cdc211
--- /dev/null
@@ -0,0 +1,75 @@
+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";
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/retest/YangModeledAnyXMLSerializationTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/retest/YangModeledAnyXMLSerializationTest.java
new file mode 100644 (file)
index 0000000..a69e45d
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * 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.impl.schema.transform.dom.serializer.retest;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+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.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.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.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+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.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.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class YangModeledAnyXMLSerializationTest 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 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");
+
+        schemaContext = RetestUtils.parseYangSources(
+                new File(getClass().getResource("/anyxml-support/serialization/baz.yang").toURI()), new File(getClass()
+                        .getResource("/anyxml-support/serialization/yang-ext.yang").toURI()));
+    }
+
+    @Test
+    public void testSerializationOfBaz() throws Exception {
+        final Document doc = loadDocument("/anyxml-support/serialization/baz.xml");
+
+        final ContainerNode output = DomToNormalizedNodeParserFactory
+                .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext).getContainerNodeParser()
+                .parse(Collections.singletonList(doc.getDocumentElement()), schemaContext);
+
+        assertNotNull(output);
+        Optional<DataContainerChild<? extends PathArgument, ?>> child = output.getChild(new NodeIdentifier(bazQName));
+        assertTrue(child.orNull() instanceof ContainerNode);
+        ContainerNode baz = (ContainerNode) child.get();
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> bazChild = baz.getChild(new NodeIdentifier(
+                myAnyXMLDataBaz));
+        assertTrue(bazChild.orNull() instanceof YangModeledAnyXmlNode);
+        YangModeledAnyXmlNode yangModeledAnyXmlNode = (YangModeledAnyXmlNode) bazChild.get();
+
+        DataSchemaNode schemaOfAnyXmlData = yangModeledAnyXmlNode.getSchemaOfAnyXmlData();
+        SchemaNode myContainer2SchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext,
+                SchemaPath.create(true, bazQName, myContainer2QName));
+        assertTrue(myContainer2SchemaNode instanceof ContainerSchemaNode);
+        assertEquals(myContainer2SchemaNode, schemaOfAnyXmlData);
+
+        DOMResult serializationResult = writeNormalizedNode(output, schemaContext);
+        assertNotNull(serializationResult);
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreComments(true);
+        XMLUnit.setIgnoreAttributeOrder(true);
+        XMLUnit.setNormalize(true);
+
+        String expectedXMLString = toString(doc.getDocumentElement().getElementsByTagName("baz").item(0));
+        String serializationResultXMLString = toString(serializationResult.getNode());
+
+        assertXMLEqual(expectedXMLString, serializationResultXMLString);
+    }
+
+    private static DOMResult writeNormalizedNode(final ContainerNode normalized, final SchemaContext context)
+            throws IOException, XMLStreamException {
+        final Document doc = XmlDocumentUtils.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 = 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);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.xml b/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.xml
new file mode 100644 (file)
index 0000000..16029d1
--- /dev/null
@@ -0,0 +1,21 @@
+<?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
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.yang b/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.yang
new file mode 100644 (file)
index 0000000..9cd2233
--- /dev/null
@@ -0,0 +1,29 @@
+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";
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/yang-ext.yang b/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/yang-ext.yang
new file mode 100644 (file)
index 0000000..2cdc211
--- /dev/null
@@ -0,0 +1,75 @@
+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";
+    }
+}