ONOS-3562 Changing the flow instructions port to human readable format

Change-Id: Ia6b1a755bc400295600f4112cb3ebe676e533eb2
diff --git a/core/api/src/main/java/org/onosproject/net/PortNumber.java b/core/api/src/main/java/org/onosproject/net/PortNumber.java
index 8c9b134..da34944 100644
--- a/core/api/src/main/java/org/onosproject/net/PortNumber.java
+++ b/core/api/src/main/java/org/onosproject/net/PortNumber.java
@@ -15,18 +15,19 @@
  */
 package org.onosproject.net;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.primitives.UnsignedLongs;
 
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * Representation of a port number.
  */
@@ -67,6 +68,7 @@
 
         /**
          * PortNumber instance for the logical port.
+         *
          * @return {@link PortNumber}
          */
         public PortNumber instance() {
@@ -145,6 +147,38 @@
     }
 
     /**
+     * Returns PortNumber instance from String representation.
+     *
+     * @param s String representation equivalent to {@link PortNumber#toString()}
+     * @return {@link PortNumber} instance
+     * @throws IllegalArgumentException if given String was malformed
+     */
+    public static PortNumber fromString(String s) {
+        checkNotNull(s);
+        checkArgument(!s.isEmpty(), "cannot be empty");
+
+        if (isAsciiDecimal(s.charAt(0))) {
+            // unsigned decimal string
+            return portNumber(s);
+        } else if (s.startsWith("[")) {
+            // named PortNumber
+            Matcher matcher = NAMED.matcher(s);
+            checkArgument(matcher.matches(), "Invalid named PortNumber %s", s);
+
+            String name = matcher.group("name");
+            String num = matcher.group("num");
+            return portNumber(UnsignedLongs.parseUnsignedLong(num), name);
+        }
+
+        // Logical
+        if (s.startsWith("UNKNOWN(") && s.endsWith(")")) {
+            return portNumber(s.substring("UNKNOWN(".length(), s.length() - 1));
+        } else {
+            return Logical.valueOf(s).instance;
+        }
+    }
+
+    /**
      * Indicates whether or not this port number is a reserved logical one or
      * whether it corresponds to a normal physical port of a device or NIC.
      *
@@ -207,38 +241,6 @@
         return '0' <= c  && c <= '9';
     }
 
-    /**
-     * Returns PortNumber instance from String representation.
-     *
-     * @param s String representation equivalent to {@link PortNumber#toString()}
-     * @return {@link PortNumber} instance
-     * @throws IllegalArgumentException if given String was malformed
-     */
-    public static PortNumber fromString(String s) {
-        checkNotNull(s);
-        checkArgument(!s.isEmpty(), "cannot be empty");
-
-        if (isAsciiDecimal(s.charAt(0))) {
-            // unsigned decimal string
-            return portNumber(s);
-        } else if (s.startsWith("[")) {
-            // named PortNumber
-            Matcher matcher = NAMED.matcher(s);
-            checkArgument(matcher.matches(), "Invalid named PortNumber %s", s);
-
-            String name = matcher.group("name");
-            String num = matcher.group("num");
-            return portNumber(UnsignedLongs.parseUnsignedLong(num), name);
-        }
-
-        // Logical
-        if (s.startsWith("UNKNOWN(") && s.endsWith(")")) {
-            return portNumber(s.substring("UNKNOWN(".length(), s.length() - 1));
-        } else {
-            return Logical.valueOf(s).instance;
-        }
-    }
-
     @Override
     public String toString() {
         if (isLogical()) {
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
index 14555b3..4878c17 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
@@ -240,9 +240,22 @@
         String type = json.get(InstructionCodec.TYPE).asText();
 
         if (type.equals(Instruction.Type.OUTPUT.name())) {
-            PortNumber portNumber =
-                    PortNumber.portNumber(nullIsIllegal(json.get(InstructionCodec.PORT),
-                            InstructionCodec.PORT + InstructionCodec.MISSING_MEMBER_MESSAGE).asLong());
+            PortNumber portNumber;
+            if (json.get(InstructionCodec.PORT).isLong() || json.get(InstructionCodec.PORT).isInt()) {
+                portNumber = PortNumber
+                        .portNumber(nullIsIllegal(json.get(InstructionCodec.PORT)
+                                                          .asLong(), InstructionCodec.PORT
+                                                          + InstructionCodec.MISSING_MEMBER_MESSAGE));
+            } else if (json.get(InstructionCodec.PORT).isTextual()) {
+                portNumber = PortNumber
+                        .fromString(nullIsIllegal(json.get(InstructionCodec.PORT)
+                                                          .textValue(), InstructionCodec.PORT
+                                                          + InstructionCodec.MISSING_MEMBER_MESSAGE));
+            } else {
+                throw new IllegalArgumentException("Port value "
+                                                           + json.get(InstructionCodec.PORT).toString()
+                                                           + " is not supported");
+            }
             return Instructions.createOutput(portNumber);
         } else if (type.equals(Instruction.Type.DROP.name())) {
             return Instructions.createDrop();
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
index 2ec301d..268d110 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
@@ -87,8 +87,6 @@
      * Encode an L1 modification instruction.
      *
      * @param result json node that the instruction attributes are added to
-     * @param instruction The L1 instruction
-     * @param context context of the request
      */
     private void encodeL1(ObjectNode result) {
         L1ModificationInstruction instruction =
@@ -244,7 +242,7 @@
             case OUTPUT:
                 final Instructions.OutputInstruction outputInstruction =
                         (Instructions.OutputInstruction) instruction;
-                result.put(InstructionCodec.PORT, outputInstruction.port().toLong());
+                result.put(InstructionCodec.PORT, outputInstruction.port().toString());
                 break;
 
             case DROP:
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java b/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
index 9ffb3c3..4c49d64 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
@@ -93,18 +93,31 @@
      */
     private boolean matchOutputInstruction(JsonNode instructionJson,
                                            Description description) {
-        OutputInstruction instructionToMatch = (OutputInstruction) instruction;
-
         final String jsonType = instructionJson.get("type").textValue();
+        OutputInstruction instructionToMatch = (OutputInstruction) instruction;
         if (!instructionToMatch.type().name().equals(jsonType)) {
             description.appendText("type was " + jsonType);
             return false;
         }
 
-        final long jsonPort = instructionJson.get("port").asLong();
-        if (instructionToMatch.port().toLong() != jsonPort) {
-            description.appendText("port was " + jsonPort);
-            return false;
+        if (instructionJson.get("port").isLong() ||
+                instructionJson.get("port").isInt()) {
+            final long jsonPort = instructionJson.get("port").asLong();
+            if (instructionToMatch.port().toLong() != (jsonPort)) {
+                description.appendText("port was " + jsonPort);
+                return false;
+            }
+        } else if (instructionJson.get("port").isTextual()) {
+            final String jsonPort = instructionJson.get("port").textValue();
+            if (!instructionToMatch.port().toString().equals(jsonPort)) {
+                description.appendText("port was " + jsonPort);
+                return false;
+            }
+        } else {
+            final String jsonPort = instructionJson.get("port").toString();
+            description.appendText("Unmathcing types ");
+            description.appendText("instructionToMatch " + instructionToMatch.port().toString());
+            description.appendText("jsonPort " + jsonPort);
         }
 
         return true;
diff --git a/web/api/src/main/resources/definitions/Flows.json b/web/api/src/main/resources/definitions/Flows.json
index 7428d73..c803fdb 100644
--- a/web/api/src/main/resources/definitions/Flows.json
+++ b/web/api/src/main/resources/definitions/Flows.json
@@ -118,9 +118,8 @@
                       "example": "OUTPUT"
                     },
                     "port": {
-                      "type": "integer",
-                      "format": "int64",
-                      "example": -3
+                      "type": "string",
+                      "example": "CONTROLLER"
                     }
                   }
                 }
diff --git a/web/api/src/main/resources/definitions/FlowsPost.json b/web/api/src/main/resources/definitions/FlowsPost.json
index 11738da..54df923 100644
--- a/web/api/src/main/resources/definitions/FlowsPost.json
+++ b/web/api/src/main/resources/definitions/FlowsPost.json
@@ -54,9 +54,8 @@
                 "example": "OUTPUT"
               },
               "port": {
-                "type": "integer",
-                "format": "int64",
-                "example": -3
+                "type": "string",
+                "example": "CONTROLLER"
               }
             }
           }
diff --git a/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
index 8910e73..da192bd 100644
--- a/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
@@ -55,6 +55,7 @@
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.rest.resources.CoreWebApplication;
 
 import javax.ws.rs.core.MediaType;
 import java.io.InputStream;
@@ -251,6 +252,10 @@
                 .andReturn(rules.get(deviceId2)).anyTimes();
     }
 
+    public FlowsResourceTest() {
+        super(CoreWebApplication.class);
+    }
+
     /**
      * Sets up the global values for all the tests.
      */