Bug 5019: Add QName param to NormalizedNodeWriter#leafSetEntryNode
[yangtools.git] / yang / yang-data-codec-gson / src / main / java / org / opendaylight / yangtools / yang / data / codec / gson / JSONNormalizedNodeStreamWriter.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.gson;
9
10 import com.google.common.base.Preconditions;
11 import com.google.gson.stream.JsonWriter;
12 import java.io.IOException;
13 import java.net.URI;
14 import org.opendaylight.yangtools.yang.common.QName;
15 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
18 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
19 import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
20 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
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.SchemaPath;
25
26 /**
27  * This implementation will create JSON output as output stream.
28  *
29  * Values of leaf and leaf-list are NOT translated according to codecs.
30  *
31  */
32 public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
33     /**
34      * RFC6020 deviation: we are not required to emit empty containers unless they
35      * are marked as 'presence'.
36      */
37     private static final boolean DEFAULT_EMIT_EMPTY_CONTAINERS = true;
38
39     private final SchemaTracker tracker;
40     private final JSONCodecFactory codecs;
41     private final JsonWriter writer;
42     private JSONStreamWriterContext context;
43
44     private JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final JsonWriter JsonWriter, final JSONStreamWriterRootContext rootContext) {
45         this.writer = Preconditions.checkNotNull(JsonWriter);
46         this.codecs = Preconditions.checkNotNull(codecFactory);
47         this.tracker = SchemaTracker.create(codecFactory.getSchemaContext(), path);
48         this.context = Preconditions.checkNotNull(rootContext);
49     }
50
51     /**
52      * Create a new stream writer, which writes to the specified output stream.
53      *
54      * The codec factory can be reused between multiple writers.
55      *
56      * Returned writer is exclusive user of JsonWriter, which means it will start
57      * top-level JSON element and ends it.
58      *
59      * This instance of writer can be used only to emit one top level element,
60      * otherwise it will produce incorrect JSON.
61      *
62      * @param codecFactory JSON codec factory
63      * @param path Schema Path
64      * @param initialNs Initial namespace
65      * @param jsonWriter JsonWriter
66      * @return A stream writer instance
67      */
68     public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
69         return new JSONNormalizedNodeStreamWriter(codecFactory, path, jsonWriter, new JSONStreamWriterExclusiveRootContext(initialNs));
70     }
71
72     /**
73      * Create a new stream writer, which writes to the specified output stream.
74      *
75      * The codec factory can be reused between multiple writers.
76      *
77      * Returned writer can be used emit multiple top level element,
78      * but does not start / close parent JSON object, which must be done
79      * by user providing {@code jsonWriter} instance in order for
80      * JSON to be valid.
81      *
82      * @param codecFactory JSON codec factory
83      * @param path Schema Path
84      * @param initialNs Initial namespace
85      * @param jsonWriter JsonWriter
86      * @return A stream writer instance
87      */
88     public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
89         return new JSONNormalizedNodeStreamWriter(codecFactory, path, jsonWriter, new JSONStreamWriterSharedRootContext(initialNs));
90     }
91
92     @Override
93     public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
94         final LeafSchemaNode schema = tracker.leafNode(name);
95         final JSONCodec<Object> codec = codecs.codecFor(schema);
96         context.emittingChild(codecs.getSchemaContext(), writer);
97         context.writeChildJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType());
98         writeValue(value, codec);
99     }
100
101     @Override
102     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
103         tracker.startLeafSet(name);
104         context = new JSONStreamWriterListContext(context, name);
105     }
106
107     @Override
108     public void leafSetEntryNode(final QName name, final Object value) throws IOException {
109         final LeafListSchemaNode schema = tracker.leafSetEntryNode();
110         final JSONCodec<Object> codec = codecs.codecFor(schema);
111         context.emittingChild(codecs.getSchemaContext(), writer);
112         writeValue(value, codec);
113     }
114
115     @Override
116     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
117         tracker.startLeafSet(name);
118         context = new JSONStreamWriterListContext(context, name);
119     }
120
121     /*
122      * Warning suppressed due to static final constant which triggers a warning
123      * for the call to schema.isPresenceContainer().
124      */
125     @SuppressWarnings("unused")
126     @Override
127     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
128         final SchemaNode schema = tracker.startContainerNode(name);
129
130         // FIXME this code ignores presence for containers
131         // but datastore does as well and it needs be fixed first (2399)
132         context = new JSONStreamWriterNamedObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS);
133     }
134
135     @Override
136     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
137         tracker.startList(name);
138         context = new JSONStreamWriterListContext(context, name);
139     }
140
141     @Override
142     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
143         tracker.startListItem(name);
144         context = new JSONStreamWriterObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS);
145     }
146
147     @Override
148     public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
149         tracker.startList(name);
150         context = new JSONStreamWriterListContext(context, name);
151     }
152
153     @Override
154     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
155             throws IOException {
156         tracker.startListItem(identifier);
157         context = new JSONStreamWriterObjectContext(context, identifier, DEFAULT_EMIT_EMPTY_CONTAINERS);
158     }
159
160     @Override
161     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
162         tracker.startList(name);
163         context = new JSONStreamWriterListContext(context, name);
164     }
165
166     @Override
167     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
168         tracker.startChoiceNode(name);
169         context = new JSONStreamWriterInvisibleContext(context);
170     }
171
172     @Override
173     public void startAugmentationNode(final AugmentationIdentifier identifier) {
174         tracker.startAugmentationNode(identifier);
175         context = new JSONStreamWriterInvisibleContext(context);
176     }
177
178     @Override
179     public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
180         @SuppressWarnings("unused")
181         final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
182         // FIXME: should have a codec based on this :)
183
184         context.emittingChild(codecs.getSchemaContext(), writer);
185         context.writeChildJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType());
186         // FIXME this kind of serialization is incorrect since the value for AnyXml is now a DOMSource
187         writer.value(String.valueOf(value));
188     }
189
190     @Override
191     public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
192         tracker.startYangModeledAnyXmlNode(name);
193         context = new JSONStreamWriterNamedObjectContext(context, name, true);
194     }
195
196     @Override
197     public void endNode() throws IOException {
198         tracker.endNode();
199         context = context.endNode(codecs.getSchemaContext(), writer);
200
201         if(context instanceof JSONStreamWriterRootContext) {
202             context.emitEnd(writer);
203         }
204     }
205
206     private void writeValue(final Object value, final JSONCodec<Object> codec)
207             throws IOException {
208         codec.serializeToWriter(writer,value);
209     }
210
211     @Override
212     public void flush() throws IOException {
213         writer.flush();
214     }
215
216     @Override
217     public void close() throws IOException {
218         flush();
219         writer.close();
220     }
221 }