[ONOS] Compute path with Explicit path objects

Change-Id: Ib487688e15db7056283feef7720f610b2f59ad84
diff --git a/apps/pce/pcerest/src/main/java/org/onosproject/pcerest/PcePathCodec.java b/apps/pce/pcerest/src/main/java/org/onosproject/pcerest/PcePathCodec.java
index 7692c7d..92b9e5e 100644
--- a/apps/pce/pcerest/src/main/java/org/onosproject/pcerest/PcePathCodec.java
+++ b/apps/pce/pcerest/src/main/java/org/onosproject/pcerest/PcePathCodec.java
@@ -16,9 +16,22 @@
 package org.onosproject.pcerest;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.Link.State.ACTIVE;
+import static org.onosproject.net.Link.Type.DIRECT;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.pce.pceservice.ExplicitPathInfo;
 import org.onosproject.pce.pceservice.PcePath;
 import org.onosproject.pce.pceservice.DefaultPcePath;
 import org.onosproject.net.intent.constraint.BandwidthConstraint;
@@ -29,6 +42,11 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.fasterxml.jackson.databind.JsonNode;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
 /**
  * PCE path json codec.
  */
@@ -42,7 +60,13 @@
     private static final String COST = "cost";
     private static final String BANDWIDTH = "bandwidth";
     private static final String PATH_ID = "pathId";
+    private static final String EXPLICIT_PATH_INFO = "explicitPathInfo";
     private static final String MISSING_MEMBER_MESSAGE = " member is required in pce-path";
+    public static final String JSON_NOT_NULL = "JsonNode can not be null";
+    public static final byte SOURCE_DEVICEID_INDEX = 0;
+    public static final byte SOURCE_PORTNO_INDEX = 1;
+    public static final byte DESTINATION_DEVICEID_INDEX = 2;
+    public static final byte DESTINATION_PORTNO_INDEX = 3;
 
     @Override
     public PcePath decode(ObjectNode json, CodecContext context) {
@@ -114,9 +138,87 @@
             }
         }
 
+        // Retrieve explicit path info
+        JsonNode explicitPathInfo = json.get(EXPLICIT_PATH_INFO);
+        if (explicitPathInfo != null) {
+            List<ExplicitPathInfo> explicitPathInfoList =
+                    ImmutableList.copyOf(jsonNodeToExplicitPathInfo(explicitPathInfo));
+            if (explicitPathInfoList != null) {
+                resultBuilder.explicitPathInfo(explicitPathInfoList);
+            }
+        }
+
         return resultBuilder.build();
     }
 
+    private ExplicitPathInfo createListOfExplicitPathObj(JsonNode node) {
+        int explicitPathType = Integer.parseInt(node.get("type").asText());
+        DeviceId deviceId;
+        PortNumber portNo;
+        NetworkResource res;
+        LinkedList<ExplicitPathInfo> list = Lists.newLinkedList();
+        if ((explicitPathType < 0) || (explicitPathType > 1)) {
+            return null;
+        }
+        ExplicitPathInfo.Type type = ExplicitPathInfo.Type.values()[explicitPathType];
+        String subType = node.get("subtype").asText();
+        if (Integer.parseInt(subType) == 0) {
+            res = DeviceId.deviceId(node.get("value").asText());
+        } else if (Integer.parseInt(subType) == 1) {
+
+            String[] splitted = node.get("value").asText().split("/");
+
+            if (splitted[SOURCE_DEVICEID_INDEX] != null
+                    && splitted[SOURCE_PORTNO_INDEX] != null
+                    && splitted[DESTINATION_DEVICEID_INDEX] != null
+                    && splitted[DESTINATION_PORTNO_INDEX] != null) {
+                return null;
+            }
+            deviceId = DeviceId.deviceId(splitted[SOURCE_DEVICEID_INDEX]);
+            portNo = PortNumber.portNumber(splitted[SOURCE_PORTNO_INDEX]);
+            ConnectPoint cpSrc = new ConnectPoint(deviceId, portNo);
+            deviceId = DeviceId.deviceId(splitted[DESTINATION_DEVICEID_INDEX]);
+            portNo = PortNumber.portNumber(splitted[DESTINATION_PORTNO_INDEX]);
+            ConnectPoint cpDst = new ConnectPoint(deviceId, portNo);
+            res = DefaultLink.builder()
+                    .providerId(ProviderId.NONE)
+                    .src(cpSrc)
+                    .dst(cpDst)
+                    .type(DIRECT)
+                    .state(ACTIVE)
+                    .build();
+        } else {
+            return null;
+        }
+
+        return new ExplicitPathInfo(type, res);
+    }
+
+    private Collection<ExplicitPathInfo> jsonNodeToExplicitPathInfo(JsonNode explicitPathInfo) {
+        checkNotNull(explicitPathInfo, JSON_NOT_NULL);
+
+        Integer i = 0;
+        NetworkResource res;
+        LinkedList<ExplicitPathInfo> list = Lists.newLinkedList();
+        if (explicitPathInfo.isArray()) {
+            for (JsonNode node : explicitPathInfo) {
+                ExplicitPathInfo obj = createListOfExplicitPathObj(node);
+                if (obj == null) {
+                    return null;
+                }
+                list.add(obj);
+            }
+        } else {
+            ExplicitPathInfo obj = createListOfExplicitPathObj(explicitPathInfo);
+            if (obj == null) {
+                return null;
+            }
+            list.add(obj);
+        }
+
+        return Collections.unmodifiableCollection(list);
+    }
+
     @Override
     public ObjectNode encode(PcePath path, CodecContext context) {
         checkNotNull(path, "path output cannot be null");
@@ -133,6 +235,18 @@
                 .put(COST, ((CostConstraint) path.costConstraint()).type().type())
                 .put(BANDWIDTH, ((BandwidthConstraint) path.bandwidthConstraint()).bandwidth().bps());
 
+        if (path.explicitPathInfo() != null && !path.explicitPathInfo().isEmpty()) {
+            ArrayNode arrayNode = context.mapper().createArrayNode();
+            for (ExplicitPathInfo e : path.explicitPathInfo()) {
+                ObjectNode node = context.mapper()
+                        .createObjectNode()
+                        .put("type", e.type().toString())
+                        .put("value", e.value().toString());
+                arrayNode.add(node);
+            }
+            result.set(EXPLICIT_PATH_INFO, arrayNode);
+        }
+
         result.set(CONSTRAINT, constraintNode);
         return result;
     }