Support to encode and decode meter id in InstructionCodec

Change-Id: Icdac264b81a9f6edf93198cc5e729521601cbe49
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 57e73ed..20d6fac 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
@@ -37,6 +37,7 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction;
+import org.onosproject.net.meter.MeterId;
 
 import static org.onlab.util.Tools.nullIsIllegal;
 
@@ -267,6 +268,10 @@
             GroupId groupId = new DefaultGroupId(nullIsIllegal(json.get(InstructionCodec.GROUP_ID)
                     .asInt(), InstructionCodec.GROUP_ID + InstructionCodec.MISSING_MEMBER_MESSAGE));
             return Instructions.createGroup(groupId);
+        } else if (type.equals(Instruction.Type.METER.name())) {
+            MeterId meterId = MeterId.meterId(nullIsIllegal(json.get(InstructionCodec.METER_ID)
+                    .asLong(), InstructionCodec.METER_ID + InstructionCodec.MISSING_MEMBER_MESSAGE));
+            return Instructions.meterTraffic(meterId);
         } else if (type.equals(Instruction.Type.L0MODIFICATION.name())) {
             return decodeL0();
         } else if (type.equals(Instruction.Type.L1MODIFICATION.name())) {
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 eccbddb..409a2f6 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
@@ -42,7 +42,7 @@
      * Creates an instruction object encoder.
      *
      * @param instruction instruction to encode
-     * @param context codec context for the encoding
+     * @param context     codec context for the encoding
      */
     public EncodeInstructionCodecHelper(Instruction instruction, CodecContext context) {
         this.instruction = instruction;
@@ -93,20 +93,21 @@
         result.put(InstructionCodec.SUBTYPE, instruction.subtype().name());
 
         switch (instruction.subtype()) {
-        case ODU_SIGID:
-            final L1ModificationInstruction.ModOduSignalIdInstruction oduSignalIdInstruction =
-                    (L1ModificationInstruction.ModOduSignalIdInstruction) instruction;
-            OduSignalId oduSignalId = oduSignalIdInstruction.oduSignalId();
+            case ODU_SIGID:
+                final L1ModificationInstruction.ModOduSignalIdInstruction oduSignalIdInstruction =
+                        (L1ModificationInstruction.ModOduSignalIdInstruction) instruction;
+                OduSignalId oduSignalId = oduSignalIdInstruction.oduSignalId();
 
-            ObjectNode child = result.putObject("oduSignalId");
+                ObjectNode child = result.putObject("oduSignalId");
 
-            child.put(InstructionCodec.TRIBUTARY_PORT_NUMBER, oduSignalId.tributaryPortNumber());
-            child.put(InstructionCodec.TRIBUTARY_SLOT_LEN, oduSignalId.tributarySlotLength());
-            child.put(InstructionCodec.TRIBUTARY_SLOT_BITMAP, HexString.toHexString(oduSignalId.tributarySlotBitmap()));
-            break;
-        default:
-            log.info("Cannot convert L1 subtype of {}", instruction.subtype());
-            break;
+                child.put(InstructionCodec.TRIBUTARY_PORT_NUMBER, oduSignalId.tributaryPortNumber());
+                child.put(InstructionCodec.TRIBUTARY_SLOT_LEN, oduSignalId.tributarySlotLength());
+                child.put(InstructionCodec.TRIBUTARY_SLOT_BITMAP,
+                        HexString.toHexString(oduSignalId.tributarySlotBitmap()));
+                break;
+            default:
+                log.info("Cannot convert L1 subtype of {}", instruction.subtype());
+                break;
         }
     }
 
@@ -151,7 +152,7 @@
                         (L2ModificationInstruction.PushHeaderInstructions) instruction;
 
                 result.put(InstructionCodec.ETHERNET_TYPE,
-                           pushHeaderInstructions.ethernetType().toShort());
+                        pushHeaderInstructions.ethernetType().toShort());
                 break;
 
             case TUNNEL_ID:
@@ -254,6 +255,12 @@
                 result.put(InstructionCodec.GROUP_ID, groupInstruction.groupId().toString());
                 break;
 
+            case METER:
+                final Instructions.MeterInstruction meterInstruction =
+                        (Instructions.MeterInstruction) instruction;
+                result.put(InstructionCodec.METER_ID, meterInstruction.meterId().toString());
+                break;
+
             case L0MODIFICATION:
                 encodeL0(result);
                 break;
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
index ab60909..2d646b0 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
@@ -51,6 +51,7 @@
     protected static final String UDP_PORT = "udpPort";
     protected static final String TABLE_ID = "tableId";
     protected static final String GROUP_ID = "groupId";
+    protected static final String METER_ID = "meterId";
     protected static final String TRIBUTARY_PORT_NUMBER = "tributaryPortNumber";
     protected static final String TRIBUTARY_SLOT_LEN = "tributarySlotLength";
     protected static final String TRIBUTARY_SLOT_BITMAP = "tributarySlotBitmap";
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 2ed7a82..6e539a8 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
@@ -22,6 +22,7 @@
 import org.onosproject.net.OduSignalId;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
+import org.onosproject.net.flow.instructions.Instructions.MeterInstruction;
 import org.onosproject.net.flow.instructions.Instructions.NoActionInstruction;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModLambdaInstruction;
@@ -138,12 +139,35 @@
             return false;
         }
 
-        if (instructionJson.get("groupId").isInt()) {
-            final int jsonGroupId = instructionJson.get("groupId").asInt();
-            if (instructionToMatch.groupId().id() != jsonGroupId) {
-                description.appendText("groupId was " + jsonGroupId);
-                return false;
-            }
+        final int jsonGroupId = instructionJson.get("groupId").intValue();
+        if (instructionToMatch.groupId().id() != jsonGroupId) {
+            description.appendText("groupId was " + jsonGroupId);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Matches the contents of a meter instruction.
+     *
+     * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
+     * @return true if contents match, false otherwise
+     */
+    private boolean matchMeterInstruction(JsonNode instructionJson,
+                                          Description description) {
+        final String jsonType = instructionJson.get("type").textValue();
+        MeterInstruction instructionToMatch = (MeterInstruction) instruction;
+        if (!instructionToMatch.type().name().equals(jsonType)) {
+            description.appendText("type was " + jsonType);
+            return false;
+        }
+
+        final long jsonMeterId = instructionJson.get("meterId").longValue();
+        if (instructionToMatch.meterId().id() != jsonMeterId) {
+            description.appendText("meterId was " + jsonMeterId);
+            return false;
         }
 
         return true;
@@ -482,6 +506,8 @@
             return matchOutputInstruction(jsonInstruction, description);
         } else if (instruction instanceof GroupInstruction) {
             return matchGroupInstruction(jsonInstruction, description);
+        } else if (instruction instanceof MeterInstruction) {
+            return matchMeterInstruction(jsonInstruction, description);
         } else if (instruction instanceof ModLambdaInstruction) {
             return matchModLambdaInstruction(jsonInstruction, description);
         } else if (instruction instanceof ModOchSignalInstruction) {