Improve error message in UnionXmlCodec
[yangtools.git] / yang / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / StreamWriterFacade.java
index fd950978043695a5ff0f62a090e7c3f23aa968b8..19c05c251c1395f9a4c1a72ed71a08e4953e06a4 100644 (file)
@@ -10,17 +10,19 @@ package org.opendaylight.yangtools.yang.data.codec.xml;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.Strings;
+import java.io.IOException;
 import java.net.URI;
-import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import javax.xml.XMLConstants;
 import javax.xml.namespace.NamespaceContext;
 import javax.xml.stream.XMLStreamConstants;
 import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
 import javax.xml.stream.XMLStreamWriter;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.util.SingleChildDataNodeContainer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,6 +33,7 @@ import org.slf4j.LoggerFactory;
 final class StreamWriterFacade extends ValueWriter {
     private static final Logger LOG = LoggerFactory.getLogger(StreamWriterFacade.class);
     private static final Set<String> BROKEN_NAMESPACES = ConcurrentHashMap.newKeySet();
+    private static final Set<String> LEGACY_ATTRIBUTES = ConcurrentHashMap.newKeySet();
 
     private final XMLStreamWriter writer;
     private final RandomPrefix prefixes;
@@ -44,7 +47,6 @@ final class StreamWriterFacade extends ValueWriter {
         prefixes = new RandomPrefix(writer.getNamespaceContext());
     }
 
-    @Override
     void writeCharacters(final String text) throws XMLStreamException {
         if (!Strings.isNullOrEmpty(text)) {
             flushElement();
@@ -88,18 +90,18 @@ final class StreamWriterFacade extends ValueWriter {
     void writeStartElement(final QName qname) throws XMLStreamException {
         flushElement();
 
-        final String ns = qname.getNamespace().toString();
+        final String namespace = qname.getNamespace().toString();
         final NamespaceContext context = writer.getNamespaceContext();
         final boolean reuseNamespace;
         if (context != null) {
-            reuseNamespace = ns.equals(context.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX));
+            reuseNamespace = namespace.equals(context.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX));
         } else {
-            reuseNamespace = XMLConstants.DEFAULT_NS_PREFIX.equals(writer.getPrefix(ns));
+            reuseNamespace = XMLConstants.DEFAULT_NS_PREFIX.equals(writer.getPrefix(namespace));
         }
 
         if (!reuseNamespace) {
-            writer.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, qname.getLocalName(), ns);
-            writer.writeDefaultNamespace(ns);
+            writer.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, qname.getLocalName(), namespace);
+            writer.writeDefaultNamespace(namespace);
         } else {
             openElement = qname;
         }
@@ -115,22 +117,7 @@ final class StreamWriterFacade extends ValueWriter {
         }
     }
 
-    void writeAttributes(final Map<QName, String> attributes) throws XMLStreamException {
-        flushElement();
-        for (final Entry<QName, String> entry : attributes.entrySet()) {
-            final QName qname = entry.getKey();
-            final String namespace = qname.getNamespace().toString();
-
-            if (!Strings.isNullOrEmpty(namespace)) {
-                final String prefix = getPrefix(qname.getNamespace(), namespace);
-                writer.writeAttribute(prefix, namespace, qname.getLocalName(), entry.getValue());
-            } else {
-                writer.writeAttribute(qname.getLocalName(), entry.getValue());
-            }
-        }
-    }
-
-    private String getPrefix(final URI uri, final String str) throws XMLStreamException {
+    String getPrefix(final URI uri, final String str) throws XMLStreamException {
         final String prefix = writer.getPrefix(str);
         if (prefix != null) {
             return prefix;
@@ -172,17 +159,88 @@ final class StreamWriterFacade extends ValueWriter {
         writer.flush();
     }
 
-    void writeStreamReader(final DOMSourceXMLStreamReader reader) throws XMLStreamException {
+    void anydataWriteStreamReader(final XMLStreamReader reader) throws XMLStreamException {
         flushElement();
 
+        // Do not emit top-level element
+        int depth = 0;
         while (reader.hasNext()) {
             final int event = reader.next();
             switch (event) {
                 case XMLStreamConstants.START_ELEMENT:
-                    forwardStartElement(reader);
+                    if (depth != 0) {
+                        forwardStartElement(reader);
+                    } else {
+                        // anydata: forward namespaces only, skipping the default namespace
+                        for (int i = 0; i < reader.getNamespaceCount(); ++i) {
+                            final String prefix = reader.getNamespacePrefix(i);
+                            if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
+                                writer.writeNamespace(prefix, reader.getNamespaceURI(i));
+                            }
+                        }
+                    }
+                    ++depth;
                     break;
                 case XMLStreamConstants.END_ELEMENT:
-                    writer.writeEndElement();
+                    --depth;
+                    if (depth != 0) {
+                        writer.writeEndElement();
+                    }
+                    break;
+                case XMLStreamConstants.CHARACTERS:
+                    writer.writeCharacters(reader.getText());
+                    break;
+                case XMLStreamConstants.COMMENT:
+                case XMLStreamConstants.SPACE:
+                    // Ignore comments and insignificant whitespace
+                    break;
+                case XMLStreamConstants.START_DOCUMENT:
+                case XMLStreamConstants.END_DOCUMENT:
+                    // We are embedded: ignore start/end document events
+                    break;
+                case XMLStreamConstants.ATTRIBUTE:
+                    forwardAttributes(reader);
+                    break;
+                case XMLStreamConstants.CDATA:
+                    writer.writeCData(reader.getText());
+                    break;
+                case XMLStreamConstants.NAMESPACE:
+                    forwardNamespaces(reader);
+                    break;
+                case XMLStreamConstants.DTD:
+                case XMLStreamConstants.NOTATION_DECLARATION:
+                case XMLStreamConstants.ENTITY_DECLARATION:
+                case XMLStreamConstants.ENTITY_REFERENCE:
+                case XMLStreamConstants.PROCESSING_INSTRUCTION:
+                default:
+                    throw new IllegalStateException("Unhandled event " + event);
+            }
+        }
+    }
+
+    void anyxmlWriteStreamReader(final DOMSourceXMLStreamReader reader) throws XMLStreamException {
+        flushElement();
+
+        // Do not emit top-level element
+        int depth = 0;
+        while (reader.hasNext()) {
+            final int event = reader.next();
+            switch (event) {
+                case XMLStreamConstants.START_ELEMENT:
+                    if (depth != 0) {
+                        forwardStartElement(reader);
+                    } else {
+                        forwardNamespaces(reader);
+                        // anyxml, hence we need to forward attributes
+                        forwardAttributes(reader);
+                    }
+                    ++depth;
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    --depth;
+                    if (depth != 0) {
+                        writer.writeEndElement();
+                    }
                     break;
                 case XMLStreamConstants.PROCESSING_INSTRUCTION:
                     forwardProcessingInstruction(reader);
@@ -223,8 +281,26 @@ final class StreamWriterFacade extends ValueWriter {
         }
     }
 
-    private void forwardAttributes(final DOMSourceXMLStreamReader reader) throws XMLStreamException {
-        for (int i = 0; i < reader.getAttributeCount(); ++i) {
+    void emitNormalizedAnydata(final NormalizedAnydata anydata) throws XMLStreamException {
+        flushElement();
+        try {
+            anydata.writeTo(XMLStreamNormalizedNodeStreamWriter.create(writer, anydata.getSchemaContext(),
+                new SingleChildDataNodeContainer(anydata.getContextNode())));
+        } catch (IOException e) {
+            throw new XMLStreamException("Failed to emit anydata " + anydata, e);
+        }
+    }
+
+    static void warnLegacyAttribute(final String localName) {
+        if (LEGACY_ATTRIBUTES.add(localName)) {
+            LOG.info("Encountered annotation {} not bound to module. Please examine the call stack and fix this "
+                    + "warning by defining a proper YANG annotation to cover it", localName,
+                    new Throwable("Call stack"));
+        }
+    }
+
+    private void forwardAttributes(final XMLStreamReader reader) throws XMLStreamException {
+        for (int i = 0, count = reader.getAttributeCount(); i < count; ++i) {
             final String localName = reader.getAttributeLocalName(i);
             final String value = reader.getAttributeValue(i);
             final String prefix = reader.getAttributePrefix(i);
@@ -236,13 +312,13 @@ final class StreamWriterFacade extends ValueWriter {
         }
     }
 
-    private void forwardNamespaces(final DOMSourceXMLStreamReader reader) throws XMLStreamException {
+    private void forwardNamespaces(final XMLStreamReader reader) throws XMLStreamException {
         for (int i = 0; i < reader.getNamespaceCount(); ++i) {
             writer.writeNamespace(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
         }
     }
 
-    private void forwardProcessingInstruction(final DOMSourceXMLStreamReader reader) throws XMLStreamException {
+    private void forwardProcessingInstruction(final XMLStreamReader reader) throws XMLStreamException {
         final String target = reader.getPITarget();
         final String data = reader.getPIData();
         if (data != null) {
@@ -252,7 +328,7 @@ final class StreamWriterFacade extends ValueWriter {
         }
     }
 
-    private void forwardStartElement(final DOMSourceXMLStreamReader reader) throws XMLStreamException {
+    private void forwardStartElement(final XMLStreamReader reader) throws XMLStreamException {
         final String localName = reader.getLocalName();
         final String prefix = reader.getPrefix();
         if (prefix != null) {