Allow shared JSONNNSWriter use 28/63928/4
authorStephen Kitt <skitt@redhat.com>
Wed, 20 Sep 2017 14:33:05 +0000 (16:33 +0200)
committerRobert Varga <nite@hq.sk>
Fri, 2 Feb 2018 15:20:01 +0000 (15:20 +0000)
JSONNormalizedNodeStreamWriter.createNestedWriter() gives the
impression that it can be used with an externally-controlled
JsonWriter, e.g. to build a JSON document containing several
serialised NormalizedNode instances. However, since it closes the
JsonWriter when closed itself, it limits the possible uses: currently,
a writer ends up having the same lifetime as a JsonWriter, even though
it doesn’t really have the same scope (because of the SchemaPath in
particular).

This patch changes the behaviour so that the provided JsonWriter has
an independent lifecycle (and needs to be closed by callers). I will
follow up with patches to the few users in ODL.

Change-Id: I9c597fe96937867cffdcbeeddd4b1507de20f2ee
Signed-off-by: Stephen Kitt <skitt@redhat.com>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit 3b56649b16a40841ff0caef191da42a0d9e73a16)

yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java

index 857badcd37f15d2d46d37928c94d4a1e4944499b..9015c8345ea4b510133faaafb23304a9913df96e 100644 (file)
@@ -40,7 +40,33 @@ import static org.w3c.dom.Node.TEXT_NODE;
  * Values of leaf and leaf-list are NOT translated according to codecs.
  *
  */
-public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+    private static final class Exclusive extends JSONNormalizedNodeStreamWriter {
+        Exclusive(final JSONCodecFactory codecFactory, final SchemaPath path, final JsonWriter writer,
+                final JSONStreamWriterRootContext rootContext) {
+            super(codecFactory, path, writer, rootContext);
+        }
+
+        @Override
+        public void close() throws IOException {
+            flush();
+            closeWriter();
+        }
+    }
+
+    private static final class Nested extends JSONNormalizedNodeStreamWriter {
+        Nested(final JSONCodecFactory codecFactory, final SchemaPath path, final JsonWriter writer,
+                final JSONStreamWriterRootContext rootContext) {
+            super(codecFactory, path, writer, rootContext);
+        }
+
+        @Override
+        public void close() throws IOException {
+            flush();
+            // The caller "owns" the writer, let them close it
+        }
+    }
+
     /**
      * RFC6020 deviation: we are not required to emit empty containers unless they
      * are marked as 'presence'.
@@ -60,8 +86,9 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
     private final JsonWriter writer;
     private JSONStreamWriterContext context;
 
-    private JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final JsonWriter JsonWriter, final JSONStreamWriterRootContext rootContext) {
-        this.writer = Preconditions.checkNotNull(JsonWriter);
+    JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final JsonWriter writer,
+            final JSONStreamWriterRootContext rootContext) {
+        this.writer = Preconditions.checkNotNull(writer);
         this.codecs = Preconditions.checkNotNull(codecFactory);
         this.tracker = SchemaTracker.create(codecFactory.getSchemaContext(), path);
         this.context = Preconditions.checkNotNull(rootContext);
@@ -76,7 +103,8 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
      * top-level JSON element and ends it.
      *
      * This instance of writer can be used only to emit one top level element,
-     * otherwise it will produce incorrect JSON.
+     * otherwise it will produce incorrect JSON. Closing this instance will close
+     * the writer too.
      *
      * @param codecFactory JSON codec factory
      * @param path Schema Path
@@ -84,8 +112,9 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
      * @param jsonWriter JsonWriter
      * @return A stream writer instance
      */
-    public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
-        return new JSONNormalizedNodeStreamWriter(codecFactory, path, jsonWriter, new JSONStreamWriterExclusiveRootContext(initialNs));
+    public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory,
+            final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
+        return new Exclusive(codecFactory, path, jsonWriter, new JSONStreamWriterExclusiveRootContext(initialNs));
     }
 
     /**
@@ -96,7 +125,8 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
      * Returned writer can be used emit multiple top level element,
      * but does not start / close parent JSON object, which must be done
      * by user providing {@code jsonWriter} instance in order for
-     * JSON to be valid.
+     * JSON to be valid. Closing this instance <strong>will not</strong>
+     * close the wrapped writer; the caller must take care of that.
      *
      * @param codecFactory JSON codec factory
      * @param path Schema Path
@@ -104,12 +134,13 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
      * @param jsonWriter JsonWriter
      * @return A stream writer instance
      */
-    public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
-        return new JSONNormalizedNodeStreamWriter(codecFactory, path, jsonWriter, new JSONStreamWriterSharedRootContext(initialNs));
+    public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory,
+            final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
+        return new Nested(codecFactory, path, jsonWriter, new JSONStreamWriterSharedRootContext(initialNs));
     }
 
     @Override
-    public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
+    public final void leafNode(final NodeIdentifier name, final Object value) throws IOException {
         final LeafSchemaNode schema = tracker.leafNode(name);
         final JSONCodec<?> codec = codecs.codecFor(schema);
         context.emittingChild(codecs.getSchemaContext(), writer);
@@ -118,13 +149,13 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
     }
 
     @Override
-    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+    public final void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startLeafSet(name);
         context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
-    public void leafSetEntryNode(final QName name, final Object value) throws IOException {
+    public final void leafSetEntryNode(final QName name, final Object value) throws IOException {
         final LeafListSchemaNode schema = tracker.leafSetEntryNode(name);
         final JSONCodec<?> codec = codecs.codecFor(schema);
         context.emittingChild(codecs.getSchemaContext(), writer);
@@ -132,7 +163,7 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
     }
 
     @Override
-    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+    public final void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startLeafSet(name);
         context = new JSONStreamWriterListContext(context, name);
     }
@@ -143,7 +174,7 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
      */
     @SuppressWarnings("unused")
     @Override
-    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+    public final void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
         final SchemaNode schema = tracker.startContainerNode(name);
 
         // FIXME this code ignores presence for containers
@@ -152,50 +183,50 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
     }
 
     @Override
-    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+    public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
         context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
-    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+    public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startListItem(name);
         context = new JSONStreamWriterObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS);
     }
 
     @Override
-    public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+    public final void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
         context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
-    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+    public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
             throws IOException {
         tracker.startListItem(identifier);
         context = new JSONStreamWriterObjectContext(context, identifier, DEFAULT_EMIT_EMPTY_CONTAINERS);
     }
 
     @Override
-    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+    public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
         context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
-    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
+    public final void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
         tracker.startChoiceNode(name);
         context = new JSONStreamWriterInvisibleContext(context);
     }
 
     @Override
-    public void startAugmentationNode(final AugmentationIdentifier identifier) {
+    public final void startAugmentationNode(final AugmentationIdentifier identifier) {
         tracker.startAugmentationNode(identifier);
         context = new JSONStreamWriterInvisibleContext(context);
     }
 
     @Override
-    public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
+    public final void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
         @SuppressWarnings("unused")
         final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
         // FIXME: should have a codec based on this :)
@@ -207,13 +238,14 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
     }
 
     @Override
-    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+    public final void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint)
+            throws IOException {
         tracker.startYangModeledAnyXmlNode(name);
         context = new JSONStreamWriterNamedObjectContext(context, name, true);
     }
 
     @Override
-    public void endNode() throws IOException {
+    public final void endNode() throws IOException {
         tracker.endNode();
         context = context.endNode(codecs.getSchemaContext(), writer);
 
@@ -222,6 +254,15 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
         }
     }
 
+    @Override
+    public final void flush() throws IOException {
+        writer.flush();
+    }
+
+    final void closeWriter() throws IOException {
+        writer.close();
+    }
+
     @SuppressWarnings("unchecked")
     private void writeValue(final Object value, final JSONCodec<?> codec)
             throws IOException {
@@ -326,15 +367,4 @@ public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStrea
 
         return Double.valueOf(numberText);
     }
-
-    @Override
-    public void flush() throws IOException {
-        writer.flush();
-    }
-
-    @Override
-    public void close() throws IOException {
-        flush();
-        writer.close();
-    }
 }