JSON serializer fixes

* Removed redundant module name prefixes to comply with RFC 7951
* Added the top level JSON node, which was missing from RESTCONF's GET output

Change-Id: I0dbe37cec23086511516e4c0c504f609a765d349
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java
index 617f871..64120c1 100644
--- a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java
@@ -40,7 +40,7 @@
     /**
      * Creates an instance of data node JSON visitor.
      *
-     * @param jb json builder
+     * @param jb      json builder
      * @param context yang serializer context
      */
     public DataNodeJsonVisitor(JsonBuilder jb, YangSerializerContext context) {
@@ -51,7 +51,7 @@
     @Override
     public void enterDataNode(DataNode dataNode,
                               DataNodeSiblingPositionType siblingType) {
-        String nodeName = getNodeNameWithModuleName(dataNode.key().schemaId());
+        String nodeName = getNodeName(dataNode);
         switch (dataNode.type()) {
             case SINGLE_INSTANCE_NODE:
                 jsonBuilder.addNodeTopHalf(nodeName, JsonNodeType.OBJECT);
@@ -79,17 +79,22 @@
             default:
                 break;
         }
+        jsonBuilder.pushModuleName(getModuleNameFromDataNode(dataNode));
     }
 
-    private String getNodeNameWithModuleName(SchemaId schemaId) {
+    private String getModuleNameFromDataNode(DataNode dataNode) {
+        String nameSpace = dataNode.key().schemaId().namespace();
+        return getModuleNameFromNameSpace(jsonSerializerContext, nameSpace);
+    }
+
+    private String getNodeName(DataNode dataNode) {
+        SchemaId schemaId = dataNode.key().schemaId();
         String nodeName = schemaId.name();
-        String nameSpace = schemaId.namespace();
-        String moduleName = getModuleNameFromNameSpace(jsonSerializerContext,
-                                                       nameSpace);
+        String moduleName = getModuleNameFromDataNode(dataNode);
 
         StringBuilder builder = new StringBuilder();
 
-        if (nameSpace != null) {
+        if (moduleName != null && !moduleName.equals(jsonBuilder.subTreeModuleName())) {
             builder.append(moduleName);
             builder.append(COLON);
         }
@@ -126,5 +131,6 @@
             default:
                 break;
         }
+        jsonBuilder.popModuleName();
     }
 }
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java
index 3dc0ff5..3b624d2 100644
--- a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java
@@ -25,6 +25,7 @@
 
 import java.io.IOException;
 import java.util.Set;
+import java.util.Stack;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Strings.isNullOrEmpty;
@@ -42,15 +43,19 @@
     private static final String COMMA = ",";
     private static final String COLON = ":";
     private static final String QUOTE = "\"";
+    private static final String ROOT_MODULE_NAME = "ROOT";
 
+    private Stack<String> moduleNameStack;
 
     public DefaultJsonBuilder(String rootName) {
         checkNotNull(rootName);
-        this.treeString = new StringBuilder(rootName);
+        treeString = new StringBuilder(rootName);
+        moduleNameStack = new Stack<>();
     }
 
     public DefaultJsonBuilder() {
-        this.treeString = new StringBuilder();
+        treeString = new StringBuilder();
+        moduleNameStack = new Stack<>();
     }
 
     @Override
@@ -141,8 +146,6 @@
 
     @Override
     public String getTreeString() {
-        removeCommaIfExist();
-        removeFirstFieldNameIfExist();
         return treeString.toString();
     }
 
@@ -178,8 +181,44 @@
     }
 
     @Override
-    public void removeExtraTerminator() {
+    public String subTreeModuleName() {
+        return moduleNameStack.peek();
+    }
+
+    @Override
+    public void pushModuleName(String moduleName) {
+        moduleNameStack.push(moduleName);
+    }
+
+    @Override
+    public void popModuleName() {
+        moduleNameStack.pop();
+    }
+
+    @Override
+    public void initializeJson() {
+        if (!moduleNameStack.empty()) {
+            moduleNameStack.removeAllElements();
+        }
+        moduleNameStack.push(ROOT_MODULE_NAME);
+        treeString.setLength(0);
+        treeString.append(LEFT_BRACE);
+    }
+
+    @Override
+    public void finalizeJson(boolean isRootTypeMultiInstance) {
         removeCommaIfExist();
+
+        if (isRootTypeMultiInstance) {
+            /*
+             * If the root node of the JSON tree is an array
+             * type, we need to close the array with the right
+             * bracket.
+             */
+            treeString.append(RIGHT_BRACKET);
+        }
+
+        treeString.append(RIGHT_BRACE);
     }
 
     private void appendField(String fieldName) {
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java
index 2f9ca39..35fd26d 100644
--- a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java
@@ -52,19 +52,21 @@
      * Converts a data node to JSON data.
      *
      * @param dataNode given data node
-     * @param context jsonserializer context
+     * @param context  jsonserializer context
      * @return JSON
      */
     public static ObjectNode convertDataNodeToJson(DataNode dataNode, YangSerializerContext context) {
         checkNotNull(dataNode, "data node cannot be null");
 
         JsonBuilder jsonBuilder = new DefaultJsonBuilder();
+        jsonBuilder.initializeJson();
         DataNodeVisitor treeNodeListener = new DataNodeJsonVisitor(jsonBuilder, context);
 
-        DataNodeSiblingPositionType siblingType = NOT_MULTI_INSTANCE_NODE;
+        DataNodeSiblingPositionType siblingType = (dataNode.type() == MULTI_INSTANCE_NODE) ?
+                FIRST_INSTANCE : NOT_MULTI_INSTANCE_NODE;
         walkDataNodeTree(treeNodeListener, dataNode, siblingType);
 
-        jsonBuilder.removeExtraTerminator();
+        jsonBuilder.finalizeJson((dataNode.type() == MULTI_INSTANCE_NODE) ? true : false);
         ObjectNode resultData = jsonBuilder.getTreeNode();
         return resultData;
     }
@@ -201,15 +203,15 @@
         Iterator it = childrenList.entrySet().iterator();
 
         while (it.hasNext()) {
-               DataNode dataNode = ((Map.Entry<NodeKey, DataNode>) it.next()).getValue();
-               String nodeName = dataNode.key().schemaId().name();
-                List<DataNode> group = groupedBucket.get(nodeName);
-                if (group == null) {
-                    group = new ArrayList<>();
-                    groupedBucket.put(nodeName, group);
-                }
+            DataNode dataNode = ((Map.Entry<NodeKey, DataNode>) it.next()).getValue();
+            String nodeName = dataNode.key().schemaId().name();
+            List<DataNode> group = groupedBucket.get(nodeName);
+            if (group == null) {
+                group = new ArrayList<>();
+                groupedBucket.put(nodeName, group);
+            }
 
-                group.add(dataNode);
+            group.add(dataNode);
 
         }
 
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java
index cb53cdd..604a6d9 100644
--- a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java
@@ -88,5 +88,46 @@
      */
     ObjectNode getTreeNode();
 
-    void removeExtraTerminator();
+    /**
+     * Returns the YANG module name of the JSON subtree that the builder
+     * is currently building. The YANG module name represents the name
+     * space of the subtree.
+     *
+     * @return YANG module name
+     */
+    String subTreeModuleName();
+
+    /**
+     * Updates the YANG module name of the JSON subtree that the builder
+     * is currently building. The YANG module name represents the name
+     * space of the subtree. This function may be called when the builder
+     * starts to build a data node.
+     *
+     * @param moduleName YANG module name of the current subtree
+     */
+    void pushModuleName(String moduleName);
+
+    /**
+     * Removes the YANG module name of the JSON subtree that the builder
+     * is currently building. This function may be called when the builder
+     * finishes building a data node.
+     */
+    void popModuleName();
+
+    /**
+     * Initializes the output JSON and emits the JSON starting symbol
+     * (e.g., the left curly bracket). This method should be the first method
+     * to be called when a JSON building process starts.
+     */
+    void initializeJson();
+
+    /**
+     * Finalizes the output JSON and emits the JSON terminating symbol
+     * (e.g., the right curly bracket). This method should be the last method
+     * to be called when a JSON building process finishes.
+     *
+     * @param isRootTypeMultiInstance true if the root node of the JSON
+     *                                tree has the multi-instance node type
+     */
+    void finalizeJson(boolean isRootTypeMultiInstance);
 }
diff --git a/serializers/json/src/test/java/org/onosproject/yang/serializers/json/JsonSerializerTest.java b/serializers/json/src/test/java/org/onosproject/yang/serializers/json/JsonSerializerTest.java
index aae2025..f32cdfb 100644
--- a/serializers/json/src/test/java/org/onosproject/yang/serializers/json/JsonSerializerTest.java
+++ b/serializers/json/src/test/java/org/onosproject/yang/serializers/json/JsonSerializerTest.java
@@ -49,9 +49,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-//import org.onosproject.yang.model.DataNode.Type;
-//import org.onosproject.yang.model.InnerNode;
-//import org.onosproject.yang.model.LeafNode;
 
 /**
  * Unit Test for Json Serializer.
@@ -69,82 +66,6 @@
         jsonSerializer = new JsonSerializer();
     }
 
-//    @Test
-//    public void jsonSerializerTest() {
-//        String path = "src/test/resources/testinput.json";
-//        DefaultCompositeStream external =
-//                new DefaultCompositeStream(null, parseInput(path));
-//        CompositeData compositeData = jsonSerializer.decode(external, context);
-//        ResourceData resourceData = compositeData.resourceData();
-//        DataNode rootNode = resourceData.dataNodes().get(0);
-//
-//        // 1. test if rootNode is SINGLE_INSTANCE_NODE.
-//        assertEquals(WRONG_TYPE, Type.SINGLE_INSTANCE_NODE, rootNode.type());
-//        InnerNode innerNode = (InnerNode) rootNode;
-//        Map<NodeKey, DataNode> nodeChilds = innerNode.childNodes();
-//        // 2. test if Root Node only have one child.
-//        assertEquals(WRONG_STRUCTURE, 1, nodeChilds.size());
-//        NodeKey nodeChildKey = convertNameStringToNodeKey("top1", "jsonlist");
-//        // 3. test if l1 is the only child of Root Node.
-//        assertEquals(WRONG_STRUCTURE, true, nodeChilds.containsKey(nodeChildKey));
-//        InnerNode nodeTop1 = (InnerNode) nodeChilds.get(nodeChildKey);
-//        Map<NodeKey, DataNode> nodeTop1Childs = nodeTop1.childNodes();
-//        // 4. test if top1 contains three childs.
-//        assertEquals(WRONG_STRUCTURE, 3, nodeTop1Childs.size());
-//        DataNode l1DataNode = getDataNode(nodeTop1Childs, "l1");
-//        // 5. test if l1 is multi_instance_node
-//        assertEquals(WRONG_TYPE, Type.MULTI_INSTANCE_NODE, l1DataNode.type());
-//
-//        InnerNode l1InnerNode = (InnerNode) l1DataNode;
-//        Map<NodeKey, DataNode> l1ChildNodes = l1InnerNode.childNodes();
-//        DataNode k1DataNode = getDataNode(l1ChildNodes, "k1");
-//        LeafNode k1LeafNode = (LeafNode) k1DataNode;
-//        DataNode k2DataNode = getDataNode(l1ChildNodes, "k2");
-//        LeafNode k2LeafNode = (LeafNode) k2DataNode;
-//        DataNode k3DataNode = getDataNode(l1ChildNodes, "k3");
-//        LeafNode k3LeafNode = (LeafNode) k3DataNode;
-//        // 6. test if k1, k2, k3 are with the right value.
-//        assertEquals(WRONG_STRUCTURE, true,
-//                     k1LeafNode.asString().equals("k1value"));
-//        assertEquals(WRONG_STRUCTURE, true,
-//                     k2LeafNode.asString().equals("k2value"));
-//        assertEquals(WRONG_STRUCTURE, true,
-//                     k3LeafNode.asString().equals("k3value"));
-//
-//        // 7. test if c1 is in the right structure.
-//        DataNode c1DataNode = getDataNode(l1ChildNodes, "c1");
-//        InnerNode c1InnerNode = (InnerNode) c1DataNode;
-//        DataNode leafC1DataNode = getDataNode(c1InnerNode.childNodes(), "leaf_c1");
-//        LeafNode leafC1LeafNode = (LeafNode) leafC1DataNode;
-//        assertEquals(WRONG_STRUCTURE, 1, c1InnerNode.childNodes().size());
-//        assertEquals(WRONG_TYPE, Type.SINGLE_INSTANCE_LEAF_VALUE_NODE, leafC1DataNode.type());
-//        assertEquals(WRONG_STRUCTURE, true, leafC1LeafNode.asString().equals("c1leaf"));
-//
-//        DataNode c2DataNode = getDataNode(nodeTop1Childs, "c2");
-//        // 8. test if c2 is single_instance_node.
-//        assertEquals(WRONG_TYPE, Type.SINGLE_INSTANCE_NODE, c2DataNode.type());
-//
-//        InnerNode c2InnerNode = (InnerNode) c2DataNode;
-//        Map<NodeKey, DataNode> c2ChildNodes = c2InnerNode.childNodes();
-//        DataNode leaflist1DataNode = getDataNode(c2ChildNodes, "leaflist1");
-//        LeafNode leafList1LeafNode = (LeafNode) leaflist1DataNode;
-//        // 9. test if leaflist1 is in the right structure.
-//        assertEquals(WRONG_TYPE, Type.MULTI_INSTANCE_LEAF_VALUE_NODE, leafList1LeafNode.type());
-//
-//    }
-//
-//    @Test
-//    public void encodeTest() {
-//        String path = "src/test/resources/testinput.json";
-//        DefaultCompositeStream external =
-//                new DefaultCompositeStream(null, parseInput(path));
-//        CompositeData compositeData = jsonSerializer.decode(external, context);
-//        CompositeStream compositeStream = jsonSerializer.encode(compositeData, context);
-//        InputStream inputStream = compositeStream.resourceData();
-//        String expectString = parseJsonToString(path);
-//        assertEquals(WRONG_STRUCTURE, expectString, convertInputStreamToString(inputStream));
-//    }
-
     @Test
     public void demo1Test() {
         String path = "src/test/resources/test.json";
@@ -156,6 +77,7 @@
         ResourceId rid = resourceData.resourceId();
         DataNode rootNode = resourceData.dataNodes().get(0);
 
+
         // encode
         RuntimeContext.Builder runtimeContextBuilder = DefaultRuntimeContext.builder();
         runtimeContextBuilder.setDataFormat("JSON");
@@ -283,7 +205,7 @@
     /**
      * Converts name and name space to NodeKey.
      *
-     * @param name name
+     * @param name      name
      * @param nameSpace name space
      * @return NodeKey
      */
@@ -324,7 +246,7 @@
      * Obtain the DataNode for Map with specific name.
      *
      * @param nodeChils Map container of InnerNode
-     * @param keyName name of the DataNode which also shown in Json File.
+     * @param keyName   name of the DataNode which also shown in Json File.
      * @return DataNode
      */
     private DataNode getDataNode(Map<NodeKey, DataNode> nodeChils, String keyName) {