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;
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;
prefixes = new RandomPrefix(writer.getNamespaceContext());
}
- @Override
void writeCharacters(final String text) throws XMLStreamException {
if (!Strings.isNullOrEmpty(text)) {
flushElement();
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;
}
}
}
- 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;
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);
}
}
- 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);
}
}
- 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) {
}
}
- 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) {