[ONOS-5769] Implement encodeYdtToCompositeProtocolFormat() for JSON codec

Change-Id: Ifb68a25ae6bc3ba10e7d983d694b132a23785890
diff --git a/protocols/restconf/client/ctl/BUCK b/protocols/restconf/client/ctl/BUCK
index 0a78565..445e4b8b 100644
--- a/protocols/restconf/client/ctl/BUCK
+++ b/protocols/restconf/client/ctl/BUCK
@@ -13,6 +13,7 @@
     '//protocols/rest/api:onos-protocols-rest-api',
     '//apps/yms/api:onos-apps-yms-api',
     '//protocols/restconf/server/utils:onos-protocols-restconf-server-utils',
+    '//providers/ietfte/utils:onos-providers-ietfte-utils',
 ]
 
 osgi_jar_with_tests (
diff --git a/protocols/restconf/client/ctl/pom.xml b/protocols/restconf/client/ctl/pom.xml
index 32d2681..f4912b3 100644
--- a/protocols/restconf/client/ctl/pom.xml
+++ b/protocols/restconf/client/ctl/pom.xml
@@ -78,6 +78,12 @@
             <version>${project.version}</version>
             <type>bundle</type>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-ietfte-provider-utils</artifactId>
+            <version>${project.version}</version>
+            <type>bundle</type>
+        </dependency>
   </dependencies>
 
     <build>
diff --git a/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/JsonYdtCodec.java b/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/JsonYdtCodec.java
index dfb7294..6fda6cd 100644
--- a/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/JsonYdtCodec.java
+++ b/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/JsonYdtCodec.java
@@ -19,9 +19,12 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.commons.io.IOUtils;
 import org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils;
+import org.onosproject.provider.te.utils.YangCompositeEncodingImpl;
 import org.onosproject.yms.ych.YangCompositeEncoding;
 import org.onosproject.yms.ych.YangDataTreeCodec;
+import org.onosproject.yms.ych.YangResourceIdentifierType;
 import org.onosproject.yms.ydt.YdtBuilder;
+import org.onosproject.yms.ydt.YdtContext;
 import org.onosproject.yms.ydt.YmsOperationType;
 import org.onosproject.yms.ymsm.YmsService;
 import org.slf4j.Logger;
@@ -30,6 +33,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertYdtToJson;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.findTopNodeInCompositeYdt;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.getJsonNameFromYdtNode;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.getUriInCompositeYdt;
 import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
 
 
@@ -38,6 +45,7 @@
  */
 public class JsonYdtCodec implements YangDataTreeCodec {
     private static final String RESTCONF_ROOT = "restconf/data";
+    private static final String EMPTY_JSON_OBJECT = "{}";
 
     protected final YmsService ymsService;
 
@@ -48,23 +56,28 @@
     }
 
     @Override
-    public String encodeYdtToProtocolFormat(YdtBuilder ydtBuilder) {
-        String json = ParserUtils.convertYdtToJson(ydtBuilder.getRootNode().getName(),
-                                                   ydtBuilder.getRootNode(),
-                                                   ymsService.getYdtWalker())
-                .textValue();
-        return json;
+    public String encodeYdtToProtocolFormat(YdtBuilder builder) {
+        return convertYdtToJson(getJsonNameFromYdtNode(builder.getRootNode()),
+                                builder.getRootNode(),
+                                ymsService.getYdtWalker()).textValue();
     }
 
     @Override
-    public YangCompositeEncoding encodeYdtToCompositeProtocolFormat(YdtBuilder ydtBuilder) {
-        // Mainly for POST/PUT operation.
-        // YdtBuilder/YdtContext has YdtContextType NONE for URI,
-        // YdtContextType CREATE/MERGE/REPLACE for Resource data.
+    public YangCompositeEncoding encodeYdtToCompositeProtocolFormat(YdtBuilder builder) {
+        String uriString = getUriInCompositeYdt(builder);
+        YdtContext topNode = findTopNodeInCompositeYdt(builder);
+        if (topNode != null) {
+            ObjectNode objectNode = convertYdtToJson(getJsonNameFromYdtNode(topNode),
+                                                     topNode,
+                                                     ymsService.getYdtWalker());
+            return new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
+                                                 uriString,
+                                                 objectNode.toString());
+        }
 
-        // TODO: Implement this method in Release Ibis for TE Tunnel.
-
-        return null;
+        return new YangCompositeEncodingImpl(YangResourceIdentifierType.URI,
+                                             uriString,
+                                             EMPTY_JSON_OBJECT);
     }
 
     @Override
diff --git a/protocols/restconf/server/utils/BUCK b/protocols/restconf/server/utils/BUCK
index d1410c0..dde27c3 100644
--- a/protocols/restconf/server/utils/BUCK
+++ b/protocols/restconf/server/utils/BUCK
@@ -1,6 +1,7 @@
 COMPILE_DEPS = [
  '//lib:CORE_DEPS',
  '//apps/yms/api:onos-apps-yms-api',
+ '//apps/yms/app:onos-apps-yms-app',
 ]
 
 osgi_jar_with_tests (
diff --git a/protocols/restconf/server/utils/pom.xml b/protocols/restconf/server/utils/pom.xml
index daed4ac..b94c198 100644
--- a/protocols/restconf/server/utils/pom.xml
+++ b/protocols/restconf/server/utils/pom.xml
@@ -18,10 +18,15 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-yms</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.easymock</groupId>
             <artifactId>easymock</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>
 
-</project>
\ No newline at end of file
+</project>
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonBuilder.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonBuilder.java
index 654fff9..90a28b2 100644
--- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonBuilder.java
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonBuilder.java
@@ -144,7 +144,7 @@
             case NUMBER:
             case POJO:
             case STRING:
-                log.debug("Unimplemented node type {}", nodeType);
+                log.trace("Unimplemented node type {}", nodeType);
                 break;
 
             default:
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java
index cf4e690..0ba2da0 100755
--- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java
@@ -100,7 +100,7 @@
             case MISSING:
             case NULL:
             case POJO:
-                log.debug("Unimplemented node type {}", nodeType);
+                log.trace("Unimplemented node type {}", nodeType);
                 break;
 
             default:
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
index d02ee97..87a3d51 100644
--- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
@@ -22,6 +22,8 @@
 import org.onosproject.protocol.restconf.server.utils.exceptions.JsonParseException;
 import org.onosproject.protocol.restconf.server.utils.parser.api.JsonBuilder;
 import org.onosproject.protocol.restconf.server.utils.parser.api.NormalizedYangNode;
+import org.onosproject.yms.app.ydt.YdtExtendedContext;
+import org.onosproject.yms.app.ydt.YdtSingleInstanceLeafNode;
 import org.onosproject.yms.ydt.YdtBuilder;
 import org.onosproject.yms.ydt.YdtContext;
 import org.onosproject.yms.ydt.YdtContextOperationType;
@@ -47,6 +49,7 @@
     private static final String EQUAL = "=";
     private static final String COMMA = ",";
     private static final String COLON = ":";
+    private static final String SLASH = "/";
     private static final String URI_ENCODING_CHAR_SET = "ISO-8859-1";
     private static final String ERROR_LIST_MSG = "List/Leaf-list node should be " +
             "in format \"nodeName=key\"or \"nodeName=instance-value\"";
@@ -393,4 +396,187 @@
 
         return moduleName;
     }
+
+    /**
+     * Extracts the URI from the given YANG Data Tree (YDT). The URI is
+     * presented in string format. If no URI is found in the YDT, an
+     * empty string is returned.
+     *
+     * @param ydtBuilder the YDT from which the URI is extracted
+     * @return URI
+     */
+    public static String getUriInCompositeYdt(YdtBuilder ydtBuilder) {
+        checkNotNull(ydtBuilder, "ydt cannot be null");
+
+        StringBuilder uriBuilder = new StringBuilder();
+        YdtContext ydtNode = ydtBuilder.getRootNode().getFirstChild();
+        String currModuleName = null;
+
+        boolean isLastNodeInUri = false;
+        int levelNum = 0;
+        while (((YdtExtendedContext) ydtNode).getYdtContextOperationType() == NONE ||
+                isLastNodeInUri) {
+            currModuleName = addNodeToUri(ydtNode, currModuleName,
+                                          levelNum, uriBuilder);
+
+            if (ydtNode.getYdtType() == YdtType.MULTI_INSTANCE_NODE) {
+                if (isLastNodeInUri) {
+                    addKeyNodeToUri(ydtNode, uriBuilder);
+                    break;
+                }
+
+                YdtContext firstChild = ydtNode.getFirstChild();
+                YdtContext lastChild = ydtNode.getLastChild();
+                if ((firstChild.getYdtType() == YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE) &&
+                        ((YdtSingleInstanceLeafNode) firstChild).isKeyLeaf()) {
+                    currModuleName = addNodeToUri(firstChild,
+                                                  currModuleName,
+                                                  levelNum,
+                                                  uriBuilder);
+                    ydtNode = lastChild;
+                } else {
+                    currModuleName = addNodeToUri(lastChild,
+                                                  currModuleName,
+                                                  levelNum,
+                                                  uriBuilder);
+                    ydtNode = firstChild;
+                }
+            } else {
+                ydtNode = ydtNode.getFirstChild();
+            }
+
+            if (isLastNodeInUri) {
+                break;
+            }
+
+            if (((YdtExtendedContext) ydtNode).getYdtContextOperationType() != NONE) {
+                isLastNodeInUri = true;
+            }
+
+            levelNum++;
+        }
+
+        return uriBuilder.toString();
+    }
+
+    /**
+     * Finds the key leaf node from the given multi-instance YDT node and
+     * appends the key value to the given URI string.
+     * <p>
+     * If no key leaf node is found, then the given URI is unchanged.
+     *
+     * @param ydtNode    YDT node under which the key leaf node is found
+     * @param uriBuilder URI
+     */
+    private static void addKeyNodeToUri(YdtContext ydtNode,
+                                        StringBuilder uriBuilder) {
+        YdtContext child = ydtNode.getFirstChild();
+
+        while (child != null) {
+            if (child.getYdtType() == YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE) {
+                if (((YdtSingleInstanceLeafNode) child).isKeyLeaf()) {
+                    uriBuilder.append(EQUAL).append(ydtNode.getValue());
+                    break;
+                }
+            }
+            child = child.getNextSibling();
+        }
+    }
+
+    /**
+     * Extracts the path segment from a given YANG Data Tree (YDT) node and
+     * appends it to the given URI string.
+     *
+     * @param ydtNode        YDT node from which the URI segment is extracted
+     * @param currModuleName current YANG module name in URI
+     * @param ydtNodeDepth   depth of the YDT node's position in the tree
+     * @param uriBuilder     URI to which the URI segment appends
+     * @return YANG module name extracted from the YDT node
+     */
+    private static String addNodeToUri(YdtContext ydtNode,
+                                       String currModuleName,
+                                       int ydtNodeDepth,
+                                       StringBuilder uriBuilder) {
+        YdtType nodeType = ydtNode.getYdtType();
+
+        /*
+         * The given YDT node is the root of the YDT. Only the module name
+         * needs to be extracted and added to URI.
+         */
+        if (ydtNodeDepth == 0) {
+            String moduleName = ydtNode.getModuleNameAsNameSpace();
+            uriBuilder.append(moduleName);
+            return moduleName;
+        }
+
+        if (ydtNodeDepth == 1) {
+            String moduleName = ydtNode.getModuleNameAsNameSpace();
+            uriBuilder.append(COLON);
+            uriBuilder.append(ydtNode.getName());
+            return moduleName;
+        }
+
+        if (nodeType == YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE &&
+                ((YdtSingleInstanceLeafNode) ydtNode).isKeyLeaf()) {
+            uriBuilder.append(EQUAL).append(ydtNode.getValue());
+            return currModuleName;
+        } else {
+            uriBuilder.append(SLASH);
+        }
+
+        String moduleName = ydtNode.getModuleNameAsNameSpace();
+
+        if (currModuleName == null || !currModuleName.equals(moduleName)) {
+            uriBuilder.append(moduleName).append(COLON);
+        }
+
+        uriBuilder.append(ydtNode.getName());
+
+        return moduleName;
+    }
+
+    /**
+     * Retrieves the top data node of the subtree from the given composite
+     * YANG Data Tree (YDT) which contains both the URI path and the data
+     * subtree to which the URI points.
+     * <p>
+     * A null is returned if no data subtree is found.
+     *
+     * @param ydtBuilder the given YDT
+     * @return the top data node of the data subtree.
+     */
+    public static YdtContext findTopNodeInCompositeYdt(YdtBuilder ydtBuilder) {
+        checkNotNull(ydtBuilder, "ydt cannot be null");
+
+        YdtContext ydtNode = ydtBuilder.getRootNode().getFirstChild();
+        YdtContextOperationType opType = ((YdtExtendedContext) ydtNode).getYdtContextOperationType();
+        while (opType == NONE) {
+            if (ydtNode.getYdtType() == YdtType.MULTI_INSTANCE_NODE) {
+                YdtContext firstChild = ydtNode.getFirstChild();
+                YdtContext lastChild = ydtNode.getLastChild();
+                if (firstChild.getYdtType() == YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE &&
+                        ((YdtSingleInstanceLeafNode) firstChild).isKeyLeaf()) {
+                    ydtNode = lastChild;
+                } else {
+                    ydtNode = firstChild;
+                }
+            } else {
+                ydtNode = ydtNode.getFirstChild();
+            }
+
+            if (((YdtExtendedContext) ydtNode).getYdtContextOperationType() != NONE) {
+                // We found last node
+                break;
+            }
+
+            if (ydtNode == null) {
+                // There is no more node to find in YDT
+                return null;
+            }
+
+            opType = ((YdtExtendedContext) ydtNode).getYdtContextOperationType();
+        }
+
+        return ydtNode;
+    }
 }
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
index eeee124..9479d97 100644
--- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
@@ -81,6 +81,8 @@
             case MULTI_INSTANCE_LEAF_VALUE_NODE:
                 jsonBuilder.addNodeWithSetTopHalf(name, ydtContext.getValueSet());
                 break;
+            case LOGICAL_ROOT_NODE:
+                break;
             default:
                 throw new YdtParseException("unknown Ydt type " +
                                                     ydtContext.getYdtType());
@@ -116,6 +118,8 @@
             case MULTI_INSTANCE_LEAF_VALUE_NODE:
                 jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
                 break;
+            case LOGICAL_ROOT_NODE:
+                break;
             default:
                 throw new YdtParseException("Unknown Ydt type " +
                                                     ydtContext.getYdtType());