Merge "BUG-1281: introduce XML event stream writer"
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / xml / XmlStreamUtils.java
1 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
2
3 import com.google.common.base.Preconditions;
4
5 import java.net.URI;
6 import java.util.Map.Entry;
7
8 import javax.xml.stream.XMLStreamException;
9 import javax.xml.stream.XMLStreamWriter;
10
11 import org.opendaylight.yangtools.yang.common.QName;
12 import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
13 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
14 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
15 import org.opendaylight.yangtools.yang.data.api.Node;
16 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
17 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
18 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
19 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
20 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
25 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
26 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 public class XmlStreamUtils {
31     private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class);
32
33     public static void writeDataDocument(final XMLStreamWriter writer, final CompositeNode data, final SchemaNode schema, final XmlCodecProvider codecProvider) throws XMLStreamException {
34         //        final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
35         //        Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces");
36
37         writer.writeStartDocument();
38         writeData(writer, data, schema, codecProvider);
39         writer.writeEndDocument();
40         writer.flush();
41     }
42
43     public static void writeDataDocument(final XMLStreamWriter writer, final CompositeNode data, final XmlCodecProvider codecProvider) throws XMLStreamException {
44         writeDataDocument(writer, data, null, codecProvider);
45     }
46
47     public static void write(final XMLStreamWriter writer, final InstanceIdentifier id) throws XMLStreamException {
48         Preconditions.checkNotNull(writer, "Writer may not be null");
49         Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
50
51         final RandomPrefix prefixes = new RandomPrefix();
52         final String str = XmlUtils.encodeIdentifier(prefixes, id);
53
54         for (Entry<URI, String> e: prefixes.getPrefixes()) {
55             writer.writeNamespace(e.getValue(), e.getKey().toString());
56         }
57         writer.writeCharacters(str);
58     }
59
60     public static void writeData(final XMLStreamWriter writer, final Node<?> data, final SchemaNode schema, final XmlCodecProvider codecProvider) throws XMLStreamException {
61         final QName qname = data.getNodeType();
62         final String pfx = qname.getPrefix() != null ? qname.getPrefix() : "";
63         final String ns;
64         if (qname.getNamespace() != null) {
65             ns = qname.getNamespace().toString();
66         } else {
67             ns = "";
68         }
69
70         writer.writeStartElement(pfx, qname.getLocalName(), ns);
71         if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) {
72             for (Entry<QName, String> attribute : ((AttributesContainer) data).getAttributes().entrySet()) {
73                 writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue());
74             }
75         }
76
77         if (data instanceof SimpleNode<?>) {
78             // Simple node
79             if (schema instanceof LeafListSchemaNode) {
80                 writeValue(writer, ((LeafListSchemaNode) schema).getType(), codecProvider, data.getValue());
81             } else if (schema instanceof LeafSchemaNode) {
82                 writeValue(writer, ((LeafSchemaNode) schema).getType(), codecProvider, data.getValue());
83             } else {
84                 Object value = data.getValue();
85                 if (value != null) {
86                     writer.writeCharacters(String.valueOf(value));
87                 }
88             }
89         } else {
90             // CompositeNode
91             for (Node<?> child : ((CompositeNode) data).getValue()) {
92                 DataSchemaNode childSchema = null;
93                 if (schema instanceof DataNodeContainer) {
94                     childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull();
95                     if (LOG.isDebugEnabled()) {
96                         if (childSchema == null) {
97                             LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName());
98                         }
99                     }
100                 }
101
102                 writeData(writer, child, childSchema, codecProvider);
103             }
104         }
105
106         writer.writeEndElement();
107     }
108
109     public static void writeValue(final XMLStreamWriter writer, final TypeDefinition<?> type, final XmlCodecProvider codecProvider, final Object nodeValue) throws XMLStreamException {
110         TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
111         if (baseType instanceof IdentityrefTypeDefinition) {
112             if (nodeValue instanceof QName) {
113                 QName value = (QName) nodeValue;
114                 String prefix = "x";
115                 if (value.getPrefix() != null && !value.getPrefix().isEmpty()) {
116                     prefix = value.getPrefix();
117                 }
118
119                 writer.writeNamespace(prefix, value.getNamespace().toString());
120                 writer.writeCharacters(prefix + ':' + value.getLocalName());
121             } else {
122                 Object value = nodeValue;
123                 LOG.debug("Value of {}:{} is not instance of QName but is {}", baseType.getQName().getNamespace(),
124                         baseType.getQName().getLocalName(), value != null ? value.getClass() : "null");
125                 if (value != null) {
126                     writer.writeCharacters(String.valueOf(value));
127                 }
128             }
129         } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
130             if (nodeValue instanceof InstanceIdentifier) {
131                 write(writer, (InstanceIdentifier)nodeValue);
132             } else {
133                 Object value = nodeValue;
134                 LOG.debug("Value of {}:{} is not instance of InstanceIdentifier but is {}", baseType.getQName()
135                         .getNamespace(), //
136                         baseType.getQName().getLocalName(), value != null ? value.getClass() : "null");
137                 if (value != null) {
138                     writer.writeCharacters(String.valueOf(value));
139                 }
140             }
141         } else {
142             if (nodeValue != null) {
143                 final TypeDefinitionAwareCodec<Object, ?> codec = codecProvider.codecFor(baseType);
144                 String text;
145                 if (codec != null) {
146                     try {
147                         text = codec.serialize(nodeValue);
148                     } catch (ClassCastException e) {
149                         LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", nodeValue, baseType, e);
150                         text = String.valueOf(nodeValue);
151                     }
152                 } else {
153                     LOG.error("Failed to find codec for {}, falling back to using stream", baseType);
154                     text = String.valueOf(nodeValue);
155                 }
156                 writer.writeCharacters(text);
157             }
158         }
159     }
160 }