Merge "Bug 2157 - Race condition when adding a RPC implementation with an output"
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / xml / XMLStreamNormalizedNodeStreamWriter.java
index b965682d2399f9faeea8d819e2f4f4e96c7fb9ad..7f2c3019d35af9356115f33ff3ecc3da77e7a20f 100644 (file)
@@ -7,16 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
 
-import com.google.common.base.Preconditions;
+import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX;
 
+import com.google.common.base.Preconditions;
 import java.io.IOException;
-import java.util.ArrayDeque;
-import java.util.Deque;
-
-import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
-
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
@@ -24,14 +20,9 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 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.NormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
+import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
-import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
 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.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
@@ -45,23 +36,15 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
  * {@link XMLStreamWriter}, resulting in a RFC 6020 XML encoding.
  */
 public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
-    private static final XmlStreamUtils UTILS = XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER);
 
-    private final Deque<Object> schemaStack = new ArrayDeque<>();
     private final XMLStreamWriter writer;
-    private final DataNodeContainer root;
+    private final SchemaTracker tracker;
+    private final XmlStreamUtils streamUtils;
 
     private XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
         this.writer = Preconditions.checkNotNull(writer);
-
-        DataNodeContainer current = context;
-        for (QName qname : path.getPathFromRoot()) {
-            final DataSchemaNode child = current.getDataChildByName(qname);
-            Preconditions.checkArgument(child instanceof DataNodeContainer);
-            current = (DataNodeContainer) child;
-        }
-
-        this.root = current;
+        this.tracker = SchemaTracker.create(context, path);
+        this.streamUtils = XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, context);
     }
 
     /**
@@ -84,43 +67,37 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
      * @return A new {@link NormalizedNodeStreamWriter}
      */
     public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
-        final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
-        Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces");
         return new XMLStreamNormalizedNodeStreamWriter(writer, context, path);
     }
 
-    private final Object getParent() {
-        if (schemaStack.isEmpty()) {
-            return root;
+    private void writeStartElement( QName qname) throws XMLStreamException {
+        String ns = qname.getNamespace().toString();
+        String parentNs = writer.getNamespaceContext().getNamespaceURI(DEFAULT_NS_PREFIX);
+        writer.writeStartElement(DEFAULT_NS_PREFIX, qname.getLocalName(), ns);
+        if (!ns.equals(parentNs)) {
+            writer.writeDefaultNamespace(ns);
         }
-        return schemaStack.peek();
-    }
-
-    private final SchemaNode getSchema(final PathArgument name) {
-        final Object parent = getParent();
-        Preconditions.checkState(parent instanceof DataNodeContainer);
-
-        final QName qname = name.getNodeType();
-        final SchemaNode schema = ((DataNodeContainer)parent).getDataChildByName(qname);
-        Preconditions.checkArgument(schema != null, "Could not find schema for node %s", qname);
-        return schema;
-    }
-
-    private void push(final Object schema) {
-        schemaStack.push(schema);
     }
 
     private void writeElement(final QName qname, final TypeDefinition<?> type, final Object value) throws IOException {
-        final String ns = qname.getNamespace().toString();
+        try {
+            writeStartElement(qname);
+            if (value != null) {
+                streamUtils.writeValue(writer, type, value);
+            }
+            writer.writeEndElement();
+        } catch (XMLStreamException e) {
+            throw new IOException("Failed to emit element", e);
+        }
+    }
 
+    private void writeElement(final QName qname, final SchemaNode schemaNode, final Object value) throws IOException {
         try {
+            writeStartElement(qname);
             if (value != null) {
-                writer.writeStartElement(ns, qname.getLocalName());
-                UTILS.writeValue(writer, type, value);
-                writer.writeEndElement();
-            } else {
-                writer.writeEmptyElement(ns, qname.getLocalName());
+                streamUtils.writeValue(writer, schemaNode, value);
             }
+            writer.writeEndElement();
         } catch (XMLStreamException e) {
             throw new IOException("Failed to emit element", e);
         }
@@ -128,59 +105,42 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
 
     private void startElement(final QName qname) throws IOException {
         try {
-            writer.writeStartElement(qname.getNamespace().toString(), qname.getLocalName());
+            writeStartElement(qname);
         } catch (XMLStreamException e) {
             throw new IOException("Failed to start element", e);
         }
     }
 
     private void startList(final NodeIdentifier name) {
-        final SchemaNode schema = getSchema(name);
-        Preconditions.checkArgument(schema instanceof ListSchemaNode, "Node %s is not a list", schema.getPath());
-        push(schema);
+        tracker.startList(name);
     }
 
     private void startListItem(final PathArgument name) throws IOException {
-        final Object schema = getParent();
-        Preconditions.checkArgument(schema instanceof ListSchemaNode, "List item is not appropriate");
+        tracker.startListItem(name);
         startElement(name.getNodeType());
-        push(schema);
     }
 
     @Override
     public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
-        final SchemaNode schema = getSchema(name);
-
-        Preconditions.checkArgument(schema instanceof LeafSchemaNode, "Node %s is not a leaf", schema.getPath());
-        final TypeDefinition<?> type = ((LeafSchemaNode) schema).getType();
-        writeElement(schema.getQName(), type, value);
+        final LeafSchemaNode schema = tracker.leafNode(name);
+        writeElement(schema.getQName(), schema, value);
     }
 
     @Override
     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
-        final SchemaNode schema = getSchema(name);
-
-        Preconditions.checkArgument(schema instanceof LeafListSchemaNode, "Node %s is not a leaf-list", schema.getPath());
-        push(schema);
+        tracker.startLeafSet(name);
     }
 
     @Override
     public void leafSetEntryNode(final Object value) throws IOException {
-        final Object parent = getParent();
-
-        Preconditions.checkArgument(parent instanceof LeafListSchemaNode, "Not currently in a leaf-list");
-        final LeafListSchemaNode schema = (LeafListSchemaNode) parent;
-        writeElement(schema.getQName(), schema.getType(), value);
+        final LeafListSchemaNode schema = tracker.leafSetEntryNode();
+        writeElement(schema.getQName(), schema, value);
     }
 
     @Override
     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
-        final SchemaNode schema = getSchema(name);
-        final QName qname = schema.getQName();
-
-        Preconditions.checkArgument(schema instanceof ContainerSchemaNode, "Node %s is not a container", schema.getPath());
-        startElement(qname);
-        push(schema);
+        final SchemaNode schema = tracker.startContainerNode(name);
+        startElement(schema.getQName());
     }
 
     @Override
@@ -210,38 +170,24 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
 
     @Override
     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
-        final SchemaNode schema = getSchema(name);
-
-        Preconditions.checkArgument(schema instanceof ChoiceNode, "Node %s is not a choice", schema.getPath());
-        push(schema);
+        tracker.startChoiceNode(name);
     }
 
     @Override
-    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
-        final Object parent = getParent();
-
-        Preconditions.checkArgument(parent instanceof AugmentationTarget, "Augmentation not allowed under %s", parent);
-        final AugmentationSchema schema = SchemaUtils.findSchemaForAugment((AugmentationTarget) parent, identifier.getPossibleChildNames());
-        push(schema);
+    public void startAugmentationNode(final AugmentationIdentifier identifier) {
+        tracker.startAugmentationNode(identifier);
     }
 
     @Override
     public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
-        final SchemaNode schema = getSchema(name);
-
-        Preconditions.checkArgument(schema instanceof AnyXmlSchemaNode, "Node %s is not anyxml", schema.getPath());
-
+        final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
         final QName qname = schema.getQName();
-        final String ns = qname.getNamespace().toString();
-
         try {
+            writeStartElement(qname);
             if (value != null) {
-                writer.writeStartElement(ns, qname.getLocalName());
-                UTILS.writeValue(writer, (Node<?>)value, schema);
-                writer.writeEndElement();
-            } else {
-                writer.writeEmptyElement(ns, qname.getLocalName());
+                streamUtils.writeValue(writer, (Node<?>)value, schema);
             }
+            writer.writeEndElement();
         } catch (XMLStreamException e) {
             throw new IOException("Failed to emit element", e);
         }
@@ -249,12 +195,12 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
 
     @Override
     public void endNode() throws IOException {
-        final Object schema = schemaStack.pop();
+        final Object schema = tracker.endNode();
 
         try {
             if (schema instanceof ListSchemaNode) {
                 // For lists, we only emit end element on the inner frame
-                final Object parent = getParent();
+                final Object parent = tracker.getParent();
                 if (parent == schema) {
                     writer.writeEndElement();
                 }