Switch XML value encoding
[yangtools.git] / yang / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / XMLStreamNormalizedNodeStreamWriter.java
1 /*
2  * Copyright (c) 2014 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 package org.opendaylight.yangtools.yang.data.codec.xml;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.io.IOException;
13 import java.util.Map;
14 import javax.xml.stream.XMLStreamException;
15 import javax.xml.stream.XMLStreamWriter;
16 import javax.xml.transform.dom.DOMSource;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
22 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
23 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
24 import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
25 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
27 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
28 import org.w3c.dom.Node;
29
30 /**
31  * A {@link NormalizedNodeStreamWriter} which translates the events into an {@link XMLStreamWriter},
32  * resulting in a RFC 6020 XML encoding. There are 2 versions of this class, one that takes a
33  * SchemaContext and encodes values appropriately according to the YANG schema. The other is
34  * schema-less and merely outputs values using toString. The latter is intended for debugging
35  * where doesn't have a SchemaContext available and isn't meant for production use.
36  */
37 public abstract class XMLStreamNormalizedNodeStreamWriter<T> implements NormalizedNodeStreamAttributeWriter {
38     private final @NonNull StreamWriterFacade facade;
39
40     XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer) {
41         facade = new StreamWriterFacade(writer);
42     }
43
44     /**
45      * Create a new writer with the specified context as its root.
46      *
47      * @param writer Output {@link XMLStreamWriter}
48      * @param context Associated {@link SchemaContext}.
49      * @return A new {@link NormalizedNodeStreamWriter}
50      */
51     public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
52             final SchemaContext context) {
53         return create(writer, context, context);
54     }
55
56     /**
57      * Create a new writer with the specified context and rooted at the specified node.
58      *
59      * @param writer Output {@link XMLStreamWriter}
60      * @param context Associated {@link SchemaContext}.
61      * @param rootNode Root node
62      * @return A new {@link NormalizedNodeStreamWriter}
63      */
64     public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context,
65             final DataNodeContainer rootNode) {
66         return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context, SchemaTracker.create(rootNode));
67     }
68
69     /**
70      * Create a new writer with the specified context and rooted in the specified schema path.
71      *
72      * @param writer Output {@link XMLStreamWriter}
73      * @param context Associated {@link SchemaContext}.
74      * @param path path
75      * @return A new {@link NormalizedNodeStreamWriter}
76      */
77     public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context,
78             final SchemaPath path) {
79         return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context, SchemaTracker.create(context, path));
80     }
81
82     /**
83      * Create a new schema-less writer. Note that this version is intended for debugging
84      * where doesn't have a SchemaContext available and isn't meant for production use.
85      *
86      * @param writer Output {@link XMLStreamWriter}
87      *
88      * @return A new {@link NormalizedNodeStreamWriter}
89      */
90     public static @NonNull NormalizedNodeStreamWriter createSchemaless(final XMLStreamWriter writer) {
91         return new SchemalessXMLStreamNormalizedNodeStreamWriter(writer);
92     }
93
94     abstract void startList(NodeIdentifier name);
95
96     abstract void startListItem(PathArgument name) throws IOException;
97
98     abstract String encodeValue(@NonNull ValueWriter xmlWriter, @NonNull Object value, T context)
99             throws XMLStreamException;
100
101     final void writeValue(final @NonNull Object value, final T context) throws IOException {
102         try {
103             facade.writeCharacters(encodeValue(facade, value, context));
104         } catch (XMLStreamException e) {
105             throw new IOException("Failed to write value", e);
106         }
107     }
108
109     final void startElement(final QName qname) throws IOException {
110         try {
111             facade.writeStartElement(qname);
112         } catch (XMLStreamException e) {
113             throw new IOException("Failed to start element", e);
114         }
115     }
116
117     final void endElement() throws IOException {
118         try {
119             facade.writeEndElement();
120         } catch (XMLStreamException e) {
121             throw new IOException("Failed to end element", e);
122         }
123     }
124
125     final void anyxmlValue(final DOMSource domSource) throws IOException {
126         if (domSource != null) {
127             final Node domNode = requireNonNull(domSource.getNode());
128             try {
129                 facade.writeStreamReader(new DOMSourceXMLStreamReader(domSource));
130             } catch (XMLStreamException e) {
131                 throw new IOException("Unable to transform anyXml value: " + domNode, e);
132             }
133         }
134     }
135
136     @Override
137     public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
138         startListItem(name);
139     }
140
141     @Override
142     public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
143             throws IOException {
144         startListItem(identifier);
145     }
146
147     @Override
148     public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
149         startList(name);
150     }
151
152     @Override
153     public final void startMapNode(final NodeIdentifier name, final int childSizeHint) {
154         startList(name);
155     }
156
157     @Override
158     public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
159         startList(name);
160     }
161
162     @Override
163     public final void close() throws IOException {
164         try {
165             facade.close();
166         } catch (XMLStreamException e) {
167             throw new IOException("Failed to close writer", e);
168         }
169     }
170
171     @Override
172     public final void flush() throws IOException {
173         try {
174             facade.flush();
175         } catch (XMLStreamException e) {
176             throw new IOException("Failed to flush writer", e);
177         }
178     }
179
180     @Override
181     public final void attributes(final Map<QName, String> attributes) throws IOException {
182         if (!attributes.isEmpty()) {
183             try {
184                 facade.writeAttributes(attributes);
185             } catch (final XMLStreamException e) {
186                 throw new IOException("Unable to emit attributes " + attributes, e);
187             }
188         }
189     }
190 }