Merge "very basic tests for yang-binding-util"
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / 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.impl.codec.xml;
9
10 import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX;
11
12 import com.google.common.base.Preconditions;
13 import java.io.IOException;
14 import javax.xml.stream.XMLStreamException;
15 import javax.xml.stream.XMLStreamWriter;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.data.api.Node;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
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.NormalizedNodeStreamWriter;
23 import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
24 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
30 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
32 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
33
34 /**
35  * A {@link NormalizedNodeStreamWriter} which translates the events into an
36  * {@link XMLStreamWriter}, resulting in a RFC 6020 XML encoding.
37  */
38 public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
39     private static final XmlStreamUtils UTILS = XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER);
40
41     private final XMLStreamWriter writer;
42     private final SchemaTracker tracker;
43
44     private XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
45         this.writer = Preconditions.checkNotNull(writer);
46         this.tracker = SchemaTracker.create(context, path);
47     }
48
49     /**
50      * Create a new writer with the specified context as its root.
51      *
52      * @param writer Output {@link XMLStreamWriter}
53      * @param context Associated {@link SchemaContext}.
54      * @return A new {@link NormalizedNodeStreamWriter}
55      */
56     public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context) {
57         return create( writer, context, SchemaPath.ROOT);
58     }
59
60     /**
61      * Create a new writer with the specified context and rooted in the specified schema path
62      *
63      * @param writer Output {@link XMLStreamWriter}
64      * @param context Associated {@link SchemaContext}.
65      *
66      * @return A new {@link NormalizedNodeStreamWriter}
67      */
68     public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
69         return new XMLStreamNormalizedNodeStreamWriter(writer, context, path);
70     }
71
72     private void writeStartElement( QName qname) throws XMLStreamException {
73         String ns = qname.getNamespace().toString();
74         String parentNs = writer.getNamespaceContext().getNamespaceURI(DEFAULT_NS_PREFIX);
75         writer.writeStartElement(DEFAULT_NS_PREFIX, qname.getLocalName(), ns);
76         if (!ns.equals(parentNs)) {
77             writer.writeDefaultNamespace(ns);
78         }
79     }
80
81     private void writeElement(final QName qname, final TypeDefinition<?> type, final Object value) throws IOException {
82         try {
83             writeStartElement(qname);
84             if (value != null) {
85                 UTILS.writeValue(writer, type, value);
86             }
87             writer.writeEndElement();
88         } catch (XMLStreamException e) {
89             throw new IOException("Failed to emit element", e);
90         }
91     }
92
93     private void startElement(final QName qname) throws IOException {
94         try {
95             writeStartElement(qname);
96         } catch (XMLStreamException e) {
97             throw new IOException("Failed to start element", e);
98         }
99     }
100
101     private void startList(final NodeIdentifier name) {
102         tracker.startList(name);
103     }
104
105     private void startListItem(final PathArgument name) throws IOException {
106         tracker.startListItem(name);
107         startElement(name.getNodeType());
108     }
109
110     @Override
111     public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
112         final LeafSchemaNode schema = tracker.leafNode(name);
113
114         writeElement(schema.getQName(), schema.getType(), value);
115     }
116
117     @Override
118     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
119         tracker.startLeafSet(name);
120     }
121
122     @Override
123     public void leafSetEntryNode(final Object value) throws IOException {
124         final LeafListSchemaNode schema = tracker.leafSetEntryNode();
125         writeElement(schema.getQName(), schema.getType(), value);
126     }
127
128     @Override
129     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
130         final SchemaNode schema = tracker.startContainerNode(name);
131         startElement(schema.getQName());
132     }
133
134     @Override
135     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
136         startList(name);
137     }
138
139     @Override
140     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
141         startListItem(name);
142     }
143
144     @Override
145     public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
146         startList(name);
147     }
148
149     @Override
150     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IOException {
151         startListItem(identifier);
152     }
153
154     @Override
155     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
156         startList(name);
157     }
158
159     @Override
160     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
161         tracker.startChoiceNode(name);
162     }
163
164     @Override
165     public void startAugmentationNode(final AugmentationIdentifier identifier) {
166         tracker.startAugmentationNode(identifier);
167     }
168
169     @Override
170     public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
171         final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
172         final QName qname = schema.getQName();
173         try {
174             writeStartElement(qname);
175             if (value != null) {
176                 UTILS.writeValue(writer, (Node<?>)value, schema);
177             }
178             writer.writeEndElement();
179         } catch (XMLStreamException e) {
180             throw new IOException("Failed to emit element", e);
181         }
182     }
183
184     @Override
185     public void endNode() throws IOException {
186         final Object schema = tracker.endNode();
187
188         try {
189             if (schema instanceof ListSchemaNode) {
190                 // For lists, we only emit end element on the inner frame
191                 final Object parent = tracker.getParent();
192                 if (parent == schema) {
193                     writer.writeEndElement();
194                 }
195             } else if (schema instanceof ContainerSchemaNode) {
196                 // Emit container end element
197                 writer.writeEndElement();
198             }
199         } catch (XMLStreamException e) {
200             throw new IOException("Failed to end element", e);
201         }
202     }
203
204     @Override
205     public void close() throws IOException {
206         try {
207             writer.close();
208         } catch (XMLStreamException e) {
209             throw new IOException("Failed to close writer", e);
210         }
211     }
212
213     @Override
214     public void flush() throws IOException {
215         try {
216             writer.flush();
217         } catch (XMLStreamException e) {
218             throw new IOException("Failed to flush writer", e);
219         }
220     }
221 }