[ONOS-6165] Demo 1: Integration - Json Serializer Encode failed
Fix the problem with encoder failed to process JSON's array child.

Change-Id: I9825836e03f1d69e05066fbbef91b8971b6a9a37
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 4344df5..f93bfee 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
@@ -174,6 +174,11 @@
         return node;
     }
 
+    @Override
+    public void removeExtraTerminator() {
+        removeCommaIfExist();
+    }
+
     private void appendField(String fieldName) {
         if (fieldName != null && !fieldName.isEmpty()) {
             treeString.append(QUOTE);
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 c0e7ccc..7127553 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
@@ -21,19 +21,15 @@
 import org.onosproject.yang.model.InnerNode;
 import org.onosproject.yang.model.NodeKey;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE;
-import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_NODE;
-import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_NODE;
-import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.FIRST_INSTANCE;
-import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.LAST_INSTANCE;
-import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.MIDDLE_INSTANCE;
-import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.NOT_MULTI_INSTANCE_NODE;
-import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.SINGLE_INSTANCE_IN_MULTI_NODE;
-import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.UNKNOWN_TYPE;
+import static org.onosproject.yang.model.DataNode.Type.*;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.*;
 
 /**
  * Utilities for converting Data Nodes into JSON format.
@@ -59,6 +55,7 @@
         DataNodeSiblingPositionType siblingType = NOT_MULTI_INSTANCE_NODE;
         walkDataNodeTree(treeNodeListener, dataNode, siblingType);
 
+        jsonBuilder.removeExtraTerminator();
         ObjectNode resultData = jsonBuilder.getTreeNode();
         return resultData;
     }
@@ -102,8 +99,10 @@
         DataNodeSiblingPositionType prevChildType = UNKNOWN_TYPE;
         DataNodeSiblingPositionType currChildType = UNKNOWN_TYPE;
 
-        Iterator it = childrenList.entrySet().iterator();
-        DataNode currChild = ((Map.Entry<NodeKey, DataNode>) it.next()).getValue();
+        List<DataNode> sortedChildList = sortChildrenList(childrenList);
+        checkNotNull(sortedChildList, "sorted children list cannot be null");
+        Iterator<DataNode> it = sortedChildList.iterator();
+        DataNode currChild = it.next();
         DataNode nextChild = null;
         boolean lastChildNotProcessed = true;
         while (lastChildNotProcessed) {
@@ -114,7 +113,7 @@
              * this info the walker.
              */
             if (it.hasNext()) {
-                nextChild = ((Map.Entry<NodeKey, DataNode>) it.next()).getValue();
+                nextChild = it.next();
             } else {
                 /*
                  * Current child is the last child.
@@ -143,22 +142,22 @@
         DataNodeSiblingPositionType curChildSiblingType = UNKNOWN_TYPE;
         switch (prevChildType) {
             case UNKNOWN_TYPE:
+            case LAST_INSTANCE:
+            case NOT_MULTI_INSTANCE_NODE:
                 /*
-                 * If type of previous child is unknown, that means
-                 * the current child is the first sibling. If the next
-                 * child is null, then that means the current child is
-                 * the only child.
+                 * If type of previous child is unknown or last instance,
+                 * that means the current child is the first sibling. If
+                 * the next child is null or has a different node name,
+                 * then that means the current child is the only child.
                  */
-                if (nextChild == null) {
+                if (nextChild == null ||
+                        !nextChild.key().schemaId().name().
+                                equals(currChild.key().schemaId().name())) {
                     curChildSiblingType = SINGLE_INSTANCE_IN_MULTI_NODE;
                 } else {
                     curChildSiblingType = FIRST_INSTANCE;
                 }
                 break;
-            case NOT_MULTI_INSTANCE_NODE:
-            case LAST_INSTANCE:
-                curChildSiblingType = FIRST_INSTANCE;
-                break;
             case FIRST_INSTANCE:
             case MIDDLE_INSTANCE:
                 /*
@@ -177,7 +176,38 @@
             default:
                 curChildSiblingType = UNKNOWN_TYPE;
         }
-
         return curChildSiblingType;
     }
+
+    private static List<DataNode> sortChildrenList(
+            Map<NodeKey, DataNode> childrenList) {
+        if (childrenList == null || childrenList.isEmpty()) {
+            // the children list is either not yet created or empty.
+            return null;
+        }
+
+        List<DataNode> sortedList = new ArrayList<>();
+        Map<String, List<DataNode>> groupedBucket = new HashMap<>();
+
+        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);
+                }
+
+                group.add(dataNode);
+
+        }
+
+        for (Map.Entry<String, List<DataNode>> entry : groupedBucket.entrySet()) {
+            sortedList.addAll(entry.getValue());
+        }
+
+        return sortedList;
+    }
 }
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 98f162b..cb53cdd 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
@@ -87,4 +87,6 @@
      * @return the final ObjectNode JSON tree after build operations
      */
     ObjectNode getTreeNode();
+
+    void removeExtraTerminator();
 }
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 6d4b165..b2025bd 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
@@ -16,6 +16,8 @@
 
 package org.onosproject.yang.serializers.json;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.commons.io.IOUtils;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -25,10 +27,15 @@
 import org.onosproject.yang.model.LeafNode;
 import org.onosproject.yang.model.NodeKey;
 import org.onosproject.yang.model.ResourceData;
+import org.onosproject.yang.model.ResourceId;
 import org.onosproject.yang.model.SchemaId;
 import org.onosproject.yang.runtime.CompositeData;
 import org.onosproject.yang.runtime.CompositeStream;
+import org.onosproject.yang.runtime.DefaultCompositeData;
 import org.onosproject.yang.runtime.DefaultCompositeStream;
+import org.onosproject.yang.runtime.DefaultResourceData;
+import org.onosproject.yang.runtime.DefaultRuntimeContext;
+import org.onosproject.yang.runtime.RuntimeContext;
 import org.onosproject.yang.runtime.YangSerializer;
 import org.onosproject.yang.runtime.YangSerializerContext;
 
@@ -137,6 +144,41 @@
         assertEquals(WRONG_STRUCTURE, expectString, convertInputStreamToString(inputStream));
     }
 
+    @Test
+    public void demo1Test() {
+        String path = "src/test/resources/test.json";
+        // decode
+        DefaultCompositeStream external =
+                new DefaultCompositeStream("demo1:device", parseInput(path));
+        CompositeData compositeData = jsonSerializer.decode(external, context);
+        ResourceData resourceData = compositeData.resourceData();
+        ResourceId rid = resourceData.resourceId();
+        DataNode rootNode = resourceData.dataNodes().get(0);
+
+        // encode
+        RuntimeContext.Builder runtimeContextBuilder = DefaultRuntimeContext.builder();
+        runtimeContextBuilder.setDataFormat("JSON");
+        DefaultResourceData.Builder resourceDataBuilder = DefaultResourceData.builder();
+        resourceDataBuilder.addDataNode(rootNode);
+        resourceDataBuilder.resourceId(rid);
+
+        ResourceData resourceDataOutput = resourceDataBuilder.build();
+        DefaultCompositeData.Builder compositeDataBuilder = DefaultCompositeData.builder();
+        compositeDataBuilder.resourceData(resourceDataOutput);
+        CompositeData compositeData1 = compositeDataBuilder.build();
+        // CompositeData --- YangRuntimeService ---> CompositeStream.
+        CompositeStream compositeStreamOutPut = jsonSerializer.encode(compositeData1,
+                                                                      context);
+        InputStream inputStreamOutput = compositeStreamOutPut.resourceData();
+        ObjectNode rootNodeOutput;
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            rootNodeOutput = (ObjectNode) mapper.readTree(inputStreamOutput);
+        } catch (IOException e) {
+            System.out.println("inputstream failed to parse");
+        }
+    }
+
     /**
      * Reads JSON contents from file path and returns input stream.
      *
diff --git a/serializers/json/src/test/resources/demo1.json b/serializers/json/src/test/resources/demo1.json
new file mode 100644
index 0000000..77714fd
--- /dev/null
+++ b/serializers/json/src/test/resources/demo1.json
@@ -0,0 +1,35 @@
+{
+
+    "device": [
+      {
+        "deviceid": "string1-deviceid",
+        "Customs-supervisor": "string2",
+        "Merchandiser-supervisor": "string3",
+        "Warehouse-supervisor": [
+          "string4.1",
+          "string4.2",
+          "string4.3"
+        ],
+        "Trading-supervisor": "string4",
+        "Employee-id": [
+          "string4.1",
+          "string4.2",
+          "string4.3"
+        ],
+        "Material-supervisor": [
+          {
+            "name": "string5",
+            "departmentId": "string6"
+          }
+        ],
+        "Purchasing-supervisor": {
+          "purchasing-specialist": "string7",
+          "support": [
+            "string8.1",
+            "string8.2",
+            "string8.3"
+          ]
+        }
+      }
+    ]
+}
diff --git a/serializers/json/src/test/resources/demo1.yang b/serializers/json/src/test/resources/demo1.yang
new file mode 100644
index 0000000..feeccd6
--- /dev/null
+++ b/serializers/json/src/test/resources/demo1.yang
@@ -0,0 +1,63 @@
+module demo1 {
+    yang-version 1;
+    namespace "namespace1";
+    prefix "demo1";
+    revision "2013-07-15";
+    container device {
+        list device {
+            key deviceid;
+            leaf deviceid {
+                type string;
+            }
+    	    leaf Customs-supervisor {
+        	type string;
+        	description "name of the customs-supervisor.";
+    	    }
+
+    	    leaf Merchandiser-supervisor {
+        	type string;
+        	description "name of merchandiser-supervisor";
+    	    }
+
+    	    list Material-supervisor {
+        	key "name";
+        	leaf name {
+            		type string;
+            		description "name of logistics-supervisor";
+        	}
+
+        	leaf departmentId {
+            		type string;
+            		description "name of department";
+        	}
+    	    }
+
+    	    container Purchasing-supervisor {
+        	leaf purchasing-specialist {
+            		type string;
+            		description "name of the purchasing-specialist person";
+        	}
+
+        	leaf-list support {
+            		type string;
+            		description "name of the support person";
+        	}
+    	    }
+
+    	    leaf-list Warehouse-supervisor {
+        	type string;
+        	description "name of the warehouse-supervisor's";
+    	    }
+
+    	    leaf Trading-supervisor {
+        	type string;
+        	description "name of the trading-supervisor";
+    	    }
+
+    	    leaf-list Employee-id {
+        	type string;
+        	description "list of the employee id";
+    	    }
+        }
+    }
+}
diff --git a/serializers/json/src/test/resources/test.json b/serializers/json/src/test/resources/test.json
new file mode 100644
index 0000000..0235997
--- /dev/null
+++ b/serializers/json/src/test/resources/test.json
@@ -0,0 +1,34 @@
+{
+    "device": [
+      {
+        "deviceid": "string1-deviceid",
+        "Customs-supervisor": "string2",
+        "Merchandiser-supervisor": "string3",
+        "Warehouse-supervisor": [
+          "string41",
+          "string42",
+          "string43"
+        ],
+        "Trading-supervisor": "string4",
+        "Employee-id": [
+          "string41",
+          "string42",
+          "string43"
+        ],
+        "Material-supervisor": [
+          {
+            "name": "string5",
+            "departmentId": "string6"
+          }
+        ],
+        "Purchasing-supervisor": {
+          "purchasing-specialist": "string7",
+          "support": [
+            "string81",
+            "string82",
+            "string83"
+          ]
+        }
+      }
+   ]
+}
\ No newline at end of file