Cleanup use of Guava library
[yangtools.git] / yang / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / XMLStreamWriterUtils.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.yangtools.yang.data.codec.xml;
10
11 import static com.google.common.base.Preconditions.checkArgument;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import java.util.Map.Entry;
15 import javax.annotation.Nonnull;
16 import javax.xml.stream.XMLStreamException;
17 import javax.xml.stream.XMLStreamWriter;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.common.QNameModule;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
21 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
22 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
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.TypedSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
27 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
28 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class
34  * by no means final and subject to change as more functionality is centralized here.
35  */
36 abstract class XMLStreamWriterUtils {
37     private static final Logger LOG = LoggerFactory.getLogger(XMLStreamWriterUtils.class);
38
39     static XMLStreamWriterUtils create(final SchemaContext schemaContext) {
40         return schemaContext == null ? SchemalessXMLStreamWriterUtils.INSTANCE
41                 : new SchemaAwareXMLStreamWriterUtils(schemaContext);
42     }
43
44     @VisibleForTesting
45     static void writeAttribute(final XMLStreamWriter writer, final Entry<QName, String> attribute,
46                                final RandomPrefix randomPrefix) throws XMLStreamException {
47         final QName key = attribute.getKey();
48         final String prefix = randomPrefix.encodePrefix(key.getNamespace());
49         writer.writeAttribute("xmlns:" + prefix, key.getNamespace().toString());
50         writer.writeAttribute(prefix, key.getNamespace().toString(), key.getLocalName(), attribute.getValue());
51     }
52
53     /**
54      * Write a value into a XML stream writer. This method assumes the start and end of element is
55      * emitted by the caller.
56      *
57      * @param writer XML Stream writer
58      * @param schemaNode Schema node that describes the value
59      * @param value data value
60      * @param parent module QName owning the leaf definition
61      * @throws XMLStreamException if an encoding problem occurs
62      */
63     void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode,
64             final Object value, final QNameModule parent) throws XMLStreamException {
65         if (value == null) {
66             LOG.debug("Value of {}:{} is null, not encoding it", schemaNode.getQName().getNamespace(),
67                     schemaNode.getQName().getLocalName());
68             return;
69         }
70
71         checkArgument(schemaNode instanceof TypedSchemaNode,
72             "Unable to write value for node %s, only nodes of type: leaf and leaf-list can be written at this point",
73             schemaNode.getQName());
74
75         TypeDefinition<?> type = ((TypedSchemaNode) schemaNode).getType();
76         if (type instanceof LeafrefTypeDefinition) {
77             type = getBaseTypeForLeafRef(schemaNode, (LeafrefTypeDefinition) type);
78         }
79
80         writeValue(writer, type, value, parent);
81     }
82
83     /**
84      * Write a value into a XML stream writer. This method assumes the start and end of element is
85      * emitted by the caller.
86      *
87      * @param writer XML Stream writer
88      * @param type data type. In case of leaf ref this should be the type of leaf being referenced
89      * @param value data value
90      * @param parent optional parameter of a module QName owning the leaf definition
91      * @throws XMLStreamException if an encoding problem occurs
92      */
93     private void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition<?> type,
94             final Object value, final QNameModule parent) throws XMLStreamException {
95         if (value == null) {
96             LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(),
97                     type.getQName().getLocalName());
98             return;
99         }
100
101         if (type instanceof IdentityrefTypeDefinition) {
102             write(writer, (IdentityrefTypeDefinition) type, value, parent);
103         } else if (type instanceof InstanceIdentifierTypeDefinition) {
104             write(writer, (InstanceIdentifierTypeDefinition) type, value);
105         } else {
106             final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
107             String text;
108             if (codec != null) {
109                 try {
110                     text = codec.serialize(value);
111                 } catch (ClassCastException e) {
112                     LOG.warn("Provided node value {} did not have type {} required by mapping. Using stream instead.",
113                             value, type, e);
114                     text = String.valueOf(value);
115                 }
116             } else {
117                 LOG.warn("Failed to find codec for {}, falling back to using stream", type);
118                 text = String.valueOf(value);
119             }
120             writer.writeCharacters(text);
121         }
122     }
123
124     @VisibleForTesting
125     static void write(@Nonnull final XMLStreamWriter writer, @Nonnull final IdentityrefTypeDefinition type,
126                       @Nonnull final Object value, final QNameModule parent) throws XMLStreamException {
127         if (value instanceof QName) {
128             final QName qname = (QName) value;
129
130             //in case parent is present and same as element namespace write value without namespace
131             if (qname.getNamespace().equals(parent.getNamespace())) {
132                 writer.writeCharacters(qname.getLocalName());
133             } else {
134                 final String ns = qname.getNamespace().toString();
135                 final String prefix = "x";
136                 writer.writeNamespace(prefix, ns);
137                 writer.writeCharacters(prefix + ':' + qname.getLocalName());
138             }
139
140         } else {
141             LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(),
142                     type.getQName().getLocalName(), value.getClass());
143             writer.writeCharacters(String.valueOf(value));
144         }
145     }
146
147     private void write(@Nonnull final XMLStreamWriter writer, @Nonnull final InstanceIdentifierTypeDefinition type,
148                        @Nonnull final Object value) throws XMLStreamException {
149         if (value instanceof YangInstanceIdentifier) {
150             writeInstanceIdentifier(writer, (YangInstanceIdentifier)value);
151         } else {
152             LOG.warn("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(),
153                     type.getQName().getLocalName(), value.getClass());
154             writer.writeCharacters(String.valueOf(value));
155         }
156     }
157
158     abstract TypeDefinition<?> getBaseTypeForLeafRef(SchemaNode schemaNode, LeafrefTypeDefinition type);
159
160     abstract void writeInstanceIdentifier(XMLStreamWriter writer, YangInstanceIdentifier value)
161             throws XMLStreamException;
162 }