Mass-migrate to use EffectiveModelContext
[yangtools.git] / yang / yang-data-codec-gson / src / main / java / org / opendaylight / yangtools / yang / data / codec / gson / JSONStreamWriterContext.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 static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.MoreObjects.ToStringHelper;
15 import com.google.gson.stream.JsonWriter;
16 import java.io.IOException;
17 import java.net.URI;
18 import java.util.Optional;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.common.QNameModule;
22 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
23 import org.opendaylight.yangtools.yang.model.api.Module;
24
25 /**
26  * Abstract base class for a single level of {@link JSONNormalizedNodeStreamWriter} recursion. Provides the base API
27  * towards the writer, which is then specialized by subclasses.
28  */
29 abstract class JSONStreamWriterContext {
30     private final JSONStreamWriterContext parent;
31     private final boolean mandatory;
32     private final int depth;
33
34     private boolean emittedMyself;
35     private boolean inChild;
36
37     /**
38      * Construct a new context.
39      *
40      * @param parent Parent context, usually non-null.
41      * @param mandatory Mandatory flag. If set to true, the corresponding node
42      *                  will be emitted even if it has no children.
43      */
44     protected JSONStreamWriterContext(final JSONStreamWriterContext parent, final boolean mandatory) {
45         this.mandatory = mandatory;
46         this.parent = parent;
47
48         if (parent != null) {
49             depth = parent.depth + 1;
50         } else {
51             depth = 0;
52         }
53     }
54
55     /**
56      * Write a child JSON node identifier, optionally prefixing it with the module name corresponding to its namespace.
57      *
58      * @param schema Schema context
59      * @param writer Output writer
60      * @param qname Namespace/name tuple
61      * @throws IOException when the writer reports it
62      */
63     final void writeChildJsonIdentifier(final EffectiveModelContext schema, final JsonWriter writer, final QName qname)
64             throws IOException {
65
66         final StringBuilder sb = new StringBuilder();
67         // Prepend module name if namespaces do not match
68         final QNameModule module = qname.getModule();
69         if (!module.getNamespace().equals(getNamespace())) {
70             final Optional<String> modules = schema.findModule(module).map(Module::getName);
71             checkArgument(modules.isPresent(), "Could not find module for namespace {}", module);
72             sb.append(modules.get()).append(':');
73         }
74         sb.append(qname.getLocalName());
75
76         writer.name(sb.toString());
77     }
78
79     /**
80      * Write our JSON node identifier, optionally prefixing it with the module name corresponding to its namespace.
81      *
82      * @param schema Schema context
83      * @param writer Output writer
84      * @param qname Namespace/name tuple
85      * @throws IOException when the writer reports it
86      */
87     protected final void writeMyJsonIdentifier(final EffectiveModelContext schema, final JsonWriter writer,
88             final QName qname) throws IOException {
89         parent.writeChildJsonIdentifier(schema, writer, qname);
90     }
91
92     /**
93      * Return the namespace associated with current node.
94      *
95      * @return Namespace as URI
96      */
97     protected abstract @NonNull URI getNamespace();
98
99     /**
100      * Emit the start of an element.
101      *
102      * @param schema Schema context
103      * @param writer Output writer
104      * @throws IOException when the writer reports it
105      */
106     protected abstract void emitStart(EffectiveModelContext schema, JsonWriter writer) throws IOException;
107
108     /**
109      * Emit the end of an element.
110      *
111      * @param schema Schema context
112      * @param writer Output writer
113      * @throws IOException when writer reports it
114      */
115     protected abstract void emitEnd(JsonWriter writer) throws IOException;
116
117     private void emitMyself(final EffectiveModelContext schema, final JsonWriter writer) throws IOException {
118         if (!emittedMyself) {
119             if (parent != null) {
120                 parent.emitMyself(schema, writer);
121             }
122
123             emitStart(schema, writer);
124             emittedMyself = true;
125         }
126     }
127
128     /**
129      * Invoked whenever a child node is being emitted. Checks whether this node has
130      * been emitted, and takes care of that if necessary. Also makes sure separator
131      * is emitted before a second and subsequent child.
132      *
133      * @param schema Schema context
134      * @param writer Output writer
135      * @throws IOException when writer reports it
136      */
137     final void emittingChild(final EffectiveModelContext schema, final JsonWriter writer) throws IOException {
138         checkState(!inChild, "Duplicate child encountered");
139         emitMyself(schema, writer);
140         inChild = true;
141     }
142
143     /**
144      * Invoked by the writer when it is leaving this node. Checks whether this node
145      * needs to be emitted and takes of that if necessary.
146      *
147      * @param schema Schema context
148      * @param writer Output writer
149      * @return Parent node context
150      * @throws IOException when writer reports it
151      * @throws IllegalArgumentException if this node cannot be ended (e.g. root)
152      */
153     final JSONStreamWriterContext endNode(final EffectiveModelContext schema, final JsonWriter writer)
154             throws IOException {
155         if (inChild) {
156             inChild = false;
157             return this;
158         }
159         if (!emittedMyself && mandatory) {
160             emitMyself(schema, writer);
161         }
162         if (emittedMyself) {
163             emitEnd(writer);
164         }
165         return parent;
166     }
167
168     @Override
169     public final String toString() {
170         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
171     }
172
173     protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
174         return helper;
175     }
176 }