Close JSON root context when the writer is closed
[yangtools.git] / yang / yang-data-codec-gson / src / main / java / org / opendaylight / yangtools / yang / data / codec / gson / JSONStreamWriterContext.java
index 1a5181a5789f2e81982348fc263623e0f7a7e049..fa2a7b9e8bd82bc82a77220216bc222eb2be646b 100644 (file)
@@ -7,29 +7,32 @@
  */
 package org.opendaylight.yangtools.yang.data.codec.gson;
 
-import com.google.common.base.Preconditions;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
 
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.gson.stream.JsonWriter;
 import java.io.IOException;
-import java.io.Writer;
 import java.net.URI;
-
-import javax.annotation.Nonnull;
-
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
- * Abstract base class for a single level of {@link JSONNormalizedNodeStreamWriter}
- * recursion. Provides the base API towards the writer, which is then specialized
- * by subclasses.
+ * Abstract base class for a single level of {@link JSONNormalizedNodeStreamWriter} recursion. Provides the base API
+ * towards the writer, which is then specialized by subclasses.
  */
 abstract class JSONStreamWriterContext {
     private final JSONStreamWriterContext parent;
     private final boolean mandatory;
     private final int depth;
-    private boolean emittedMyself = false;
-    private boolean haveChild = false;
+
+    private boolean emittedMyself;
+    private boolean inChild;
 
     /**
      * Construct a new context.
@@ -50,29 +53,40 @@ abstract class JSONStreamWriterContext {
     }
 
     /**
-     * Write a JSON node identifier, optionally prefixing it with the module name
-     * corresponding to its namespace.
+     * Write a child JSON node identifier, optionally prefixing it with the module name corresponding to its namespace.
      *
      * @param schema Schema context
      * @param writer Output writer
      * @param qname Namespace/name tuple
      * @throws IOException when the writer reports it
      */
-    protected final void writeJsonIdentifier(final SchemaContext schema, final Writer writer, final QName qname) throws IOException {
-        writer.append('"');
+    final void writeChildJsonIdentifier(final SchemaContext schema, final JsonWriter writer, final QName qname)
+            throws IOException {
 
+        final StringBuilder sb = new StringBuilder();
         // Prepend module name if namespaces do not match
-        final URI ns = qname.getNamespace();
-        if (!ns.equals(getNamespace())) {
-            final Module module = schema.findModuleByNamespaceAndRevision(ns, null);
-            Preconditions.checkArgument(module != null, "Could not find module for namespace {}", ns);
-
-            writer.append(module.getName());
-            writer.append(':');
+        final QNameModule module = qname.getModule();
+        if (!module.getNamespace().equals(getNamespace())) {
+            final Optional<String> modules = schema.findModule(module).map(Module::getName);
+            checkArgument(modules.isPresent(), "Could not find module for namespace {}", module);
+            sb.append(modules.get()).append(':');
         }
+        sb.append(qname.getLocalName());
 
-        writer.append(qname.getLocalName());
-        writer.append("\":");
+        writer.name(sb.toString());
+    }
+
+    /**
+     * Write our JSON node identifier, optionally prefixing it with the module name corresponding to its namespace.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @param qname Namespace/name tuple
+     * @throws IOException when the writer reports it
+     */
+    protected final void writeMyJsonIdentifier(final SchemaContext schema, final JsonWriter writer, final QName qname)
+            throws IOException {
+        parent.writeChildJsonIdentifier(schema, writer, qname);
     }
 
     /**
@@ -80,30 +94,30 @@ abstract class JSONStreamWriterContext {
      *
      * @return Namespace as URI
      */
-    protected abstract @Nonnull URI getNamespace();
+    protected abstract @NonNull URI getNamespace();
 
     /**
      * Emit the start of an element.
      *
      * @param schema Schema context
      * @param writer Output writer
-     * @throws IOException
+     * @throws IOException when the writer reports it
      */
-    protected abstract void emitStart(final SchemaContext schema, final Writer writer) throws IOException;
+    protected abstract void emitStart(SchemaContext schema, JsonWriter writer) throws IOException;
 
     /**
      * Emit the end of an element.
      *
      * @param schema Schema context
      * @param writer Output writer
-     * @throws IOException
+     * @throws IOException when writer reports it
      */
-    protected abstract void emitEnd(final Writer writer) throws IOException;
+    protected abstract void emitEnd(JsonWriter writer) throws IOException;
 
-    private final void emitMyself(final SchemaContext schema, final Writer writer, final String indent) throws IOException {
+    private void emitMyself(final SchemaContext schema, final JsonWriter writer) throws IOException {
         if (!emittedMyself) {
             if (parent != null) {
-                parent.emittingChild(schema, writer, indent);
+                parent.emitMyself(schema, writer);
             }
 
             emitStart(schema, writer);
@@ -118,23 +132,12 @@ abstract class JSONStreamWriterContext {
      *
      * @param schema Schema context
      * @param writer Output writer
-     * @param indent Indentation string
      * @throws IOException when writer reports it
      */
-    final void emittingChild(final SchemaContext schema, final Writer writer, final String indent) throws IOException {
-        emitMyself(schema, writer, indent);
-        if (haveChild) {
-            writer.append(',');
-        }
-
-        if (indent != null) {
-            writer.append('\n');
-
-            for (int i = 0; i < depth; i++) {
-                writer.append(indent);
-            }
-        }
-        haveChild = true;
+    final void emittingChild(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        checkState(!inChild, "Duplicate child encountered");
+        emitMyself(schema, writer);
+        inChild = true;
     }
 
     /**
@@ -143,19 +146,30 @@ abstract class JSONStreamWriterContext {
      *
      * @param schema Schema context
      * @param writer Output writer
-     * @param indent Indentation string
      * @return Parent node context
      * @throws IOException when writer reports it
      * @throws IllegalArgumentException if this node cannot be ended (e.g. root)
      */
-    final JSONStreamWriterContext endNode(final SchemaContext schema, final Writer writer, final String indent) throws IOException {
+    final JSONStreamWriterContext endNode(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        if (inChild) {
+            inChild = false;
+            return this;
+        }
         if (!emittedMyself && mandatory) {
-            emitMyself(schema, writer, indent);
+            emitMyself(schema, writer);
         }
-
         if (emittedMyself) {
             emitEnd(writer);
         }
         return parent;
     }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper;
+    }
 }