BUG-1704: rework state tracking 66/10866/5
authorRobert Varga <rovarga@cisco.com>
Fri, 5 Sep 2014 13:06:03 +0000 (15:06 +0200)
committerRobert Varga <rovarga@cisco.com>
Tue, 9 Sep 2014 12:21:46 +0000 (14:21 +0200)
In order to understand when a list (or container) is necessary, the
state tracking machinery needs to be reworked. This patch does exactly
that, adding a single-linked stack tracking nodes and their respective
output.

Change-Id: Iaf6ec6b25c05e0505a1dec6faad5fde2ecb791a0
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java [new file with mode: 0644]

index c7755b83bd42ac282f309014ff4d20963a4873e3..9e0224220ecaad4d0422dfb2e20b54d95f01e2f4 100644 (file)
@@ -17,9 +17,7 @@ import java.io.Writer;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.net.URI;
-import java.util.ArrayDeque;
 import java.util.Collection;
-import java.util.Deque;
 
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
@@ -28,9 +26,9 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
@@ -42,51 +40,20 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
  * FIXME: rewrite this in terms of {@link JsonWriter}.
  */
 public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
-
-    private static enum NodeType {
-        OBJECT,
-        LIST,
-        OTHER,
-    }
-
-    private static class TypeInfo {
-        private boolean hasAtLeastOneChild = false;
-        private final NodeType type;
-        private final URI uri;
-
-        public TypeInfo(final NodeType type, final URI uri) {
-            this.type = type;
-            this.uri = uri;
-        }
-
-        public void setHasAtLeastOneChild(final boolean hasChildren) {
-            this.hasAtLeastOneChild = hasChildren;
-        }
-
-        public NodeType getType() {
-            return type;
-        }
-
-        public URI getNamespace() {
-            return uri;
-        }
-
-        public boolean hasAtLeastOneChild() {
-            return hasAtLeastOneChild;
-        }
-    }
-
+    /**
+     * RFC6020 deviation: we are not required to emit empty containers unless they
+     * are marked as 'presence'.
+     */
+    private static final boolean DEFAULT_EMIT_EMPTY_CONTAINERS = true;
     private static final Collection<Class<?>> NUMERIC_CLASSES =
             ImmutableSet.<Class<?>>of(Byte.class, Short.class, Integer.class, Long.class, BigInteger.class, BigDecimal.class);
-    private final Deque<TypeInfo> stack = new ArrayDeque<>();
+
     private final SchemaContext schemaContext;
-    private final CodecFactory codecs;
     private final SchemaTracker tracker;
+    private final CodecFactory codecs;
     private final Writer writer;
     private final String indent;
-
-    private int currentDepth = 0;
-    private URI currentNamespace;
+    private JSONStreamWriterContext context;
 
     private JSONNormalizedNodeStreamWriter(final SchemaContext schemaContext,
             final Writer writer, final int indentSize) {
@@ -106,8 +73,7 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
         }
         this.codecs = CodecFactory.create(schemaContext);
         this.tracker = SchemaTracker.create(schemaContext, path);
-
-        this.currentNamespace = initialNs;
+        this.context = new JSONStreamWriterRootContext(initialNs);
     }
 
     /**
@@ -125,10 +91,11 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
      * Create a new stream writer, which writes to the specified {@link Writer}.
      *
      * @param schemaContext Schema context
+     * @param path Root schemapath
      * @param writer Output writer
      * @return A stream writer instance
      */
-    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path,final Writer writer) {
+    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path, final Writer writer) {
         return new JSONNormalizedNodeStreamWriter(schemaContext, path, writer, null, 0);
     }
 
@@ -136,11 +103,13 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
      * Create a new stream writer, which writes to the specified {@link Writer}.
      *
      * @param schemaContext Schema context
+     * @param path Root schemapath
      * @param writer Output writer
      * @param initialNs Initial namespace
      * @return A stream writer instance
      */
-    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path,final URI initialNs, final Writer writer) {
+    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path,
+            final URI initialNs, final Writer writer) {
         return new JSONNormalizedNodeStreamWriter(schemaContext, path, writer, initialNs, 0);
     }
 
@@ -161,22 +130,15 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
         final LeafSchemaNode schema = tracker.leafNode(name);
         final Codec<Object, Object> codec = codecs.codecFor(schema.getType());
 
-        separateElementFromPreviousElement();
-        writeJsonIdentifier(name);
-        currentNamespace = stack.peek().getNamespace();
+        context.emittingChild(schemaContext, writer, indent);
+        context.writeJsonIdentifier(schemaContext, writer, name.getNodeType());
         writeValue(codec.serialize(value));
-        separateNextSiblingsWithComma();
     }
 
     @Override
     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startLeafSet(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartList();
-        indentRight();
+        context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
@@ -184,87 +146,57 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
         final LeafListSchemaNode schema = tracker.leafSetEntryNode();
         final Codec<Object, Object> codec = codecs.codecFor(schema.getType());
 
-        separateElementFromPreviousElement();
+        context.emittingChild(schemaContext, writer, indent);
         writeValue(codec.serialize(value));
-        separateNextSiblingsWithComma();
     }
 
     @Override
     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
-        tracker.startContainerNode(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartObject();
-        indentRight();
+        final ContainerSchemaNode schema = tracker.startContainerNode(name);
+        context = new JSONStreamWriterNamedObjectContext(context, name, schema.isPresenceContainer() || DEFAULT_EMIT_EMPTY_CONTAINERS);
     }
 
     @Override
     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartList();
-        indentRight();
+        context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startListItem(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
-        writeStartObject();
-        indentRight();
+        context = new JSONStreamWriterObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS);
     }
 
     @Override
     public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartList();
-        indentRight();
+        context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
             throws IOException {
         tracker.startListItem(identifier);
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.OBJECT, identifier.getNodeType().getNamespace()));
-
-
-        writeStartObject();
-        indentRight();
+        context = new JSONStreamWriterObjectContext(context, identifier, DEFAULT_EMIT_EMPTY_CONTAINERS);
     }
 
     @Override
     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartList();
-        indentRight();
+        context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
-    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
         tracker.startChoiceNode(name);
-        handleInvisibleNode(name.getNodeType().getNamespace());
+        context = new JSONStreamWriterInvisibleContext(context);
     }
 
     @Override
-    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+    public void startAugmentationNode(final AugmentationIdentifier identifier) {
         tracker.startAugmentationNode(identifier);
-        handleInvisibleNode(currentNamespace);
+        context = new JSONStreamWriterInvisibleContext(context);
     }
 
     @Override
@@ -272,85 +204,15 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
         final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
         // FIXME: should have a codec based on this :)
 
-        separateElementFromPreviousElement();
-        writeJsonIdentifier(name);
-        currentNamespace = stack.peek().getNamespace();
+        context.emittingChild(schemaContext, writer, indent);
+        context.writeJsonIdentifier(schemaContext, writer, name.getNodeType());
         writeValue(value);
-        separateNextSiblingsWithComma();
     }
 
     @Override
     public void endNode() throws IOException {
         tracker.endNode();
-
-        final TypeInfo t = stack.pop();
-        switch (t.getType()) {
-        case LIST:
-            indentLeft();
-            newLine();
-            writer.append(']');
-            break;
-        case OBJECT:
-            indentLeft();
-            newLine();
-            writer.append('}');
-            break;
-        default:
-            break;
-        }
-
-        currentNamespace = stack.isEmpty() ? null : stack.peek().getNamespace();
-        separateNextSiblingsWithComma();
-    }
-
-    private void separateElementFromPreviousElement() throws IOException {
-        if (!stack.isEmpty() && stack.peek().hasAtLeastOneChild()) {
-            writer.append(',');
-        }
-        newLine();
-    }
-
-    private void newLine() throws IOException {
-        if (indent != null) {
-            writer.append('\n');
-
-            for (int i = 0; i < currentDepth; i++) {
-                writer.append(indent);
-            }
-        }
-    }
-
-    private void separateNextSiblingsWithComma() {
-        if (!stack.isEmpty()) {
-            stack.peek().setHasAtLeastOneChild(true);
-        }
-    }
-
-    /**
-     * Invisible nodes have to be also pushed to stack because of pairing of start*() and endNode() methods. Information
-     * about child existing (due to printing comma) has to be transfered to invisible node.
-     */
-    private void handleInvisibleNode(final URI uri) {
-        TypeInfo typeInfo = new TypeInfo(NodeType.OTHER, uri);
-        typeInfo.setHasAtLeastOneChild(stack.peek().hasAtLeastOneChild());
-        stack.push(typeInfo);
-    }
-
-    private void writeStartObject() throws IOException {
-        writer.append('{');
-    }
-
-    private void writeStartList() throws IOException {
-        writer.append('[');
-    }
-
-    private void writeModulName(final URI namespace) throws IOException {
-        if (this.currentNamespace == null || namespace != this.currentNamespace) {
-            Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
-            writer.append(module.getName());
-            writer.append(':');
-            currentNamespace = namespace;
-        }
+        context = context.endNode(schemaContext, writer, indent);
     }
 
     private void writeValue(final Object value) throws IOException {
@@ -365,21 +227,6 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
         }
     }
 
-    private void writeJsonIdentifier(final NodeIdentifier name) throws IOException {
-        writer.append('"');
-        writeModulName(name.getNodeType().getNamespace());
-        writer.append(name.getNodeType().getLocalName());
-        writer.append("\":");
-    }
-
-    private void indentRight() {
-        currentDepth++;
-    }
-
-    private void indentLeft() {
-        currentDepth--;
-    }
-
     @Override
     public void flush() throws IOException {
         writer.flush();
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java
new file mode 100644 (file)
index 0000000..1a5181a
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.yang.common.QName;
+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 class JSONStreamWriterContext {
+    private final JSONStreamWriterContext parent;
+    private final boolean mandatory;
+    private final int depth;
+    private boolean emittedMyself = false;
+    private boolean haveChild = false;
+
+    /**
+     * Construct a new context.
+     *
+     * @param parent Parent context, usually non-null.
+     * @param mandatory Mandatory flag. If set to true, the corresponding node
+     *                  will be emitted even if it has no children.
+     */
+    protected JSONStreamWriterContext(final JSONStreamWriterContext parent, final boolean mandatory) {
+        this.mandatory = mandatory;
+        this.parent = parent;
+
+        if (parent != null) {
+            depth = parent.depth + 1;
+        } else {
+            depth = 0;
+        }
+    }
+
+    /**
+     * Write a 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('"');
+
+        // 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(':');
+        }
+
+        writer.append(qname.getLocalName());
+        writer.append("\":");
+    }
+
+    /**
+     * Return the namespace associated with current node.
+     *
+     * @return Namespace as URI
+     */
+    protected abstract @Nonnull URI getNamespace();
+
+    /**
+     * Emit the start of an element.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @throws IOException
+     */
+    protected abstract void emitStart(final SchemaContext schema, final Writer writer) throws IOException;
+
+    /**
+     * Emit the end of an element.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @throws IOException
+     */
+    protected abstract void emitEnd(final Writer writer) throws IOException;
+
+    private final void emitMyself(final SchemaContext schema, final Writer writer, final String indent) throws IOException {
+        if (!emittedMyself) {
+            if (parent != null) {
+                parent.emittingChild(schema, writer, indent);
+            }
+
+            emitStart(schema, writer);
+            emittedMyself = true;
+        }
+    }
+
+    /**
+     * Invoked whenever a child node is being emitted. Checks whether this node has
+     * been emitted, and takes care of that if necessary. Also makes sure separator
+     * is emitted before a second and subsequent child.
+     *
+     * @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;
+    }
+
+    /**
+     * Invoked by the writer when it is leaving this node. Checks whether this node
+     * needs to be emitted and takes of that if necessary.
+     *
+     * @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 {
+        if (!emittedMyself && mandatory) {
+            emitMyself(schema, writer, indent);
+        }
+
+        if (emittedMyself) {
+            emitEnd(writer);
+        }
+        return parent;
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java
new file mode 100644 (file)
index 0000000..7f22c19
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import java.io.Writer;
+
+/**
+ * A virtual recursion level in {@link JSONNormalizedNodeStreamWriter}, used for nodes
+ * which are not emitted in the JSON representation.
+ */
+final class JSONStreamWriterInvisibleContext extends JSONStreamWriterURIContext {
+    JSONStreamWriterInvisibleContext(final JSONStreamWriterContext parent) {
+        super(Preconditions.checkNotNull(parent), parent.getNamespace());
+    }
+
+    @Override
+    protected void emitEnd(final Writer writer) {
+        // No-op
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java
new file mode 100644 (file)
index 0000000..ed7bd09
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.io.Writer;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A single recursion level of {@link JSONNormalizedNodeStreamWriter} representing
+ * a list.
+ */
+final class JSONStreamWriterListContext extends JSONStreamWriterQNameContext {
+    protected JSONStreamWriterListContext(final JSONStreamWriterContext parent, final NodeIdentifier id) {
+        super(Preconditions.checkNotNull(parent), id.getNodeType(), false);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final Writer writer) throws IOException {
+        writeJsonIdentifier(schema, writer, getQName());
+        writer.append('[');
+    }
+
+    @Override
+    protected void emitEnd(final Writer writer) throws IOException {
+        writer.append(']');
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java
new file mode 100644 (file)
index 0000000..f5e5d48
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A recursion level of {@link JSONNormalizedNodeStreamWriter}, which represents
+ * a JSON object which has to be prefixed with its identifier -- such as a
+ * container.
+ */
+final class JSONStreamWriterNamedObjectContext extends JSONStreamWriterObjectContext {
+    protected JSONStreamWriterNamedObjectContext(final JSONStreamWriterContext parent, final PathArgument arg, final boolean mandatory) {
+        super(parent, arg, mandatory);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final Writer writer) throws IOException {
+        writeJsonIdentifier(schema, writer, getQName());
+        super.emitStart(schema, writer);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java
new file mode 100644 (file)
index 0000000..d12f044
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A recursion level of {@link JSONNormalizedNodeStreamWriter}, which represents
+ * a JSON object which does not have to be prefixed with its identifier -- such
+ * as when it is in a containing list.
+ */
+class JSONStreamWriterObjectContext extends JSONStreamWriterQNameContext {
+    protected JSONStreamWriterObjectContext(final JSONStreamWriterContext parent, final PathArgument arg, final boolean mandatory) {
+        super(Preconditions.checkNotNull(parent), arg.getNodeType(), mandatory);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final Writer writer) throws IOException {
+        writer.append('{');
+    }
+
+    @Override
+    protected void emitEnd(final Writer writer) throws IOException {
+        writer.append('}');
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java
new file mode 100644 (file)
index 0000000..ad4ad6b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Abstract base class for {@link JSONNormalizedNodeStreamWriter} recursion
+ * levels which emit a QName-identified node.
+ */
+abstract class JSONStreamWriterQNameContext extends JSONStreamWriterContext {
+    private final QName qname;
+
+    protected JSONStreamWriterQNameContext(final JSONStreamWriterContext parent, final QName qname, final boolean mandatory) {
+        super(parent, mandatory);
+        this.qname = Preconditions.checkNotNull(qname);
+    }
+
+    /**
+     * Returns the node's identifier as a QName.
+     *
+     * @return QName identifier
+     */
+    protected final QName getQName() {
+        return qname;
+    }
+
+    @Override
+    protected final URI getNamespace() {
+        return qname.getNamespace();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java
new file mode 100644 (file)
index 0000000..36c3ff3
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import java.io.Writer;
+import java.net.URI;
+
+/**
+ * The root node of a particular {@link JSONNormalizedNodeStreamWriter} instance.
+ * It holds the base namespace and can never be removed from the stack.
+ */
+final class JSONStreamWriterRootContext extends JSONStreamWriterURIContext {
+    JSONStreamWriterRootContext(final URI namespace) {
+        super(null, namespace);
+    }
+
+    @Override
+    protected void emitEnd(final Writer writer) {
+        throw new IllegalArgumentException("Top-level node reached");
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java
new file mode 100644 (file)
index 0000000..06c32bf
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Abstract class tracking a virtual level of {@link JSONNormalizedNodeStreamWriter}
+ * recursion. It only tracks the namespace associated with this node.
+ */
+abstract class JSONStreamWriterURIContext extends JSONStreamWriterContext {
+    private final URI namespace;
+
+    protected JSONStreamWriterURIContext(final JSONStreamWriterContext parent, final URI namespace) {
+        super(parent, false);
+        this.namespace = namespace;
+    }
+
+    @Override
+    protected final URI getNamespace() {
+        return namespace;
+    }
+
+    @Override
+    protected final void emitStart(final SchemaContext schema, final Writer writer) throws IOException {
+        // No-op
+    }
+}
\ No newline at end of file