bec315491fc268c169ca5b41f6f1e6aaeb177778
[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 com.google.common.base.Preconditions;
11
12 import java.io.IOException;
13
14 import javax.xml.stream.XMLStreamException;
15 import javax.xml.stream.XMLStreamWriter;
16
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.data.api.Node;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
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.AnyXmlSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
31 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
33 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
34
35 /**
36  * A {@link NormalizedNodeStreamWriter} which translates the events into an
37  * {@link XMLStreamWriter}, resulting in a RFC 6020 XML encoding.
38  */
39 public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
40     private static final XmlStreamUtils UTILS = XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER);
41
42     private final XMLStreamWriter writer;
43     private final SchemaTracker tracker;
44
45     private XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
46         this.writer = Preconditions.checkNotNull(writer);
47         this.tracker = SchemaTracker.create(context, path);
48     }
49
50     /**
51      * Create a new writer with the specified context as its root.
52      *
53      * @param writer Output {@link XMLStreamWriter}
54      * @param context Associated {@link SchemaContext}.
55      * @return A new {@link NormalizedNodeStreamWriter}
56      */
57     public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context) {
58         return create( writer, context, SchemaPath.ROOT);
59     }
60
61     /**
62      * Create a new writer with the specified context and rooted in the specified schema path
63      *
64      * @param writer Output {@link XMLStreamWriter}
65      * @param context Associated {@link SchemaContext}.
66      *
67      * @return A new {@link NormalizedNodeStreamWriter}
68      */
69     public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
70         return new XMLStreamNormalizedNodeStreamWriter(writer, context, path);
71     }
72
73     private void writeElement(final QName qname, final TypeDefinition<?> type, final Object value) throws IOException {
74         final String ns = qname.getNamespace().toString();
75
76         try {
77             if (value != null) {
78                 writer.writeStartElement(ns, qname.getLocalName());
79                 UTILS.writeValue(writer, type, value);
80                 writer.writeEndElement();
81             } else {
82                 writer.writeEmptyElement(ns, qname.getLocalName());
83             }
84         } catch (XMLStreamException e) {
85             throw new IOException("Failed to emit element", e);
86         }
87     }
88
89     private void startElement(final QName qname) throws IOException {
90         try {
91             writer.writeStartElement(qname.getNamespace().toString(), qname.getLocalName());
92         } catch (XMLStreamException e) {
93             throw new IOException("Failed to start element", e);
94         }
95     }
96
97     private void startList(final NodeIdentifier name) {
98         tracker.startList(name);
99     }
100
101     private void startListItem(final PathArgument name) throws IOException {
102         tracker.startListItem(name);
103         startElement(name.getNodeType());
104     }
105
106     @Override
107     public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
108         final LeafSchemaNode schema = tracker.leafNode(name);
109
110         writeElement(schema.getQName(), schema.getType(), value);
111     }
112
113     @Override
114     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
115         tracker.startLeafSet(name);
116     }
117
118     @Override
119     public void leafSetEntryNode(final Object value) throws IOException {
120         final LeafListSchemaNode schema = tracker.leafSetEntryNode();
121         writeElement(schema.getQName(), schema.getType(), value);
122     }
123
124     @Override
125     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
126         final SchemaNode schema = tracker.startContainerNode(name);
127         startElement(schema.getQName());
128     }
129
130     @Override
131     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
132         startList(name);
133     }
134
135     @Override
136     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
137         startListItem(name);
138     }
139
140     @Override
141     public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
142         startList(name);
143     }
144
145     @Override
146     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IOException {
147         startListItem(identifier);
148     }
149
150     @Override
151     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
152         startList(name);
153     }
154
155     @Override
156     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
157         tracker.startChoiceNode(name);
158     }
159
160     @Override
161     public void startAugmentationNode(final AugmentationIdentifier identifier) {
162         tracker.startAugmentationNode(identifier);
163     }
164
165     @Override
166     public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
167         final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
168         final QName qname = schema.getQName();
169         final String ns = qname.getNamespace().toString();
170
171         try {
172             if (value != null) {
173                 writer.writeStartElement(ns, qname.getLocalName());
174                 UTILS.writeValue(writer, (Node<?>)value, schema);
175                 writer.writeEndElement();
176             } else {
177                 writer.writeEmptyElement(ns, qname.getLocalName());
178             }
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 }