Refactor criterion codec test and matcher

- codec test now uses the json matcher rather than comparing itself
- refactored the codec matcher to be less ugly
- fixed a bug in how eth types are compared

Change-Id: Iaf9980c52e98518405a23d2cdde7ccd7d4afaa9b
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java b/web/api/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java
index 3efc018..a7f6bd2 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java
@@ -34,6 +34,7 @@
 import static org.onlab.junit.TestUtils.getField;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.*;
+import static org.onosproject.codec.impl.CriterionJsonMatcher.matchesCriterion;
 
 /**
  * Unit tests for criterion codec.
@@ -81,8 +82,7 @@
     public void matchInPortTest() {
         Criterion criterion = Criteria.matchInPort(port);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("port").asLong(), is(port.toLong()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -92,8 +92,7 @@
     public void matchInPhyPortTest() {
         Criterion criterion = Criteria.matchInPhyPort(port);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("port").asLong(), is(port.toLong()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -103,8 +102,7 @@
     public void matchMetadataTest() {
         Criterion criterion = Criteria.matchMetadata(0xabcdL);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("metadata").asLong(), is(0xabcdL));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -114,8 +112,7 @@
     public void matchEthDstTest() {
         Criterion criterion = Criteria.matchEthDst(mac1);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("mac").asText(), is(mac1.toString()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -125,8 +122,7 @@
     public void matchEthSrcTest() {
         Criterion criterion = Criteria.matchEthSrc(mac1);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("mac").asText(), is(mac1.toString()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -136,8 +132,7 @@
     public void matchEthTypeTest() {
         Criterion criterion = Criteria.matchEthType((short) 0x8844);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("ethType").asInt(), is(0x8844));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -147,8 +142,7 @@
     public void matchVlanIdTest() {
         Criterion criterion = Criteria.matchVlanId(VlanId.ANY);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat((short) result.get("vlanId").asInt(), is(VlanId.ANY.toShort()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -158,8 +152,7 @@
     public void matchVlanPcpTest() {
         Criterion criterion = Criteria.matchVlanPcp((byte) 7);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("priority").asInt(), is(7));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -169,8 +162,7 @@
     public void matchIPDscpTest() {
         Criterion criterion = Criteria.matchIPDscp((byte) 63);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("ipDscp").asInt(), is(63));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -180,8 +172,7 @@
     public void matchIPEcnTest() {
         Criterion criterion = Criteria.matchIPEcn((byte) 7);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("ipEcn").asInt(), is(7));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -191,8 +182,7 @@
     public void matchIPProtocolTest() {
         Criterion criterion = Criteria.matchIPProtocol((byte) 250);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("protocol").asInt(), is(250));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -202,8 +192,7 @@
     public void matchIPSrcTest() {
         Criterion criterion = Criteria.matchIPSrc(ipPrefix4);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("ip").asText(), is(ipPrefix4.toString()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -213,8 +202,7 @@
     public void matchIPDstTest() {
         Criterion criterion = Criteria.matchIPDst(ipPrefix4);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("ip").asText(), is(ipPrefix4.toString()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -224,8 +212,7 @@
     public void matchTcpSrcTest() {
         Criterion criterion = Criteria.matchTcpSrc((short) 40000);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("tcpPort").asInt(), is(40000));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -235,8 +222,7 @@
     public void matchTcpDstTest() {
         Criterion criterion = Criteria.matchTcpDst((short) 40000);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("tcpPort").asInt(), is(40000));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -244,10 +230,9 @@
      */
     @Test
     public void matchUdpSrcTest() {
-        Criterion criterion = Criteria.matchUdpSrc((short) 40000);
+        Criterion criterion = Criteria.matchUdpSrc(40000);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("udpPort").asInt(), is(40000));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -255,10 +240,9 @@
      */
     @Test
     public void matchUdpDstTest() {
-        Criterion criterion = Criteria.matchUdpDst((short) 40000);
+        Criterion criterion = Criteria.matchUdpDst(40000);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("udpPort").asInt(), is(40000));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -266,10 +250,9 @@
      */
     @Test
     public void matchSctpSrcTest() {
-        Criterion criterion = Criteria.matchSctpSrc((short) 40000);
+        Criterion criterion = Criteria.matchSctpSrc(40000);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("sctpPort").asInt(), is(40000));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -277,10 +260,9 @@
      */
     @Test
     public void matchSctpDstTest() {
-        Criterion criterion = Criteria.matchSctpDst((short) 40000);
+        Criterion criterion = Criteria.matchSctpDst(40000);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("sctpPort").asInt(), is(40000));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -290,8 +272,7 @@
     public void matchIcmpTypeTest() {
         Criterion criterion = Criteria.matchIcmpType((byte) 250);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("icmpType").asInt(), is(250));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -301,8 +282,7 @@
     public void matchIcmpCodeTest() {
         Criterion criterion = Criteria.matchIcmpCode((byte) 250);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("icmpCode").asInt(), is(250));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -312,8 +292,7 @@
     public void matchIPv6SrcTest() {
         Criterion criterion = Criteria.matchIPv6Src(ipPrefix6);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("ip").asText(), is(ipPrefix6.toString()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -323,8 +302,7 @@
     public void matchIPv6DstTest() {
         Criterion criterion = Criteria.matchIPv6Dst(ipPrefix6);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("ip").asText(), is(ipPrefix6.toString()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -334,8 +312,7 @@
     public void matchIPv6FlowLabelTest() {
         Criterion criterion = Criteria.matchIPv6FlowLabel(0xffffe);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("flowLabel").asInt(), is(0xffffe));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -345,8 +322,7 @@
     public void matchIcmpv6TypeTest() {
         Criterion criterion = Criteria.matchIcmpv6Type((byte) 250);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("icmpv6Type").asInt(), is(250));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -356,8 +332,7 @@
     public void matchIcmpv6CodeTest() {
         Criterion criterion = Criteria.matchIcmpv6Code((byte) 250);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("icmpv6Code").asInt(), is(250));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -369,8 +344,7 @@
                 Criteria.matchIPv6NDTargetAddress(
                         Ip6Address.valueOf("1111:2222::"));
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("targetAddress").asText(), is("1111:2222::"));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -380,8 +354,7 @@
     public void matchIPv6NDSourceLinkLayerAddressTest() {
         Criterion criterion = Criteria.matchIPv6NDSourceLinkLayerAddress(mac1);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("mac").asText(), is(mac1.toString()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -391,8 +364,7 @@
     public void matchIPv6NDTargetLinkLayerAddressTest() {
         Criterion criterion = Criteria.matchIPv6NDTargetLinkLayerAddress(mac1);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("mac").asText(), is(mac1.toString()));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -402,8 +374,7 @@
     public void matchMplsLabelTest() {
         Criterion criterion = Criteria.matchMplsLabel(0xffffe);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("label").asInt(), is(0xffffe));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -423,8 +394,8 @@
             Criterion.IPv6ExthdrFlags.UNSEQ.getValue();
         Criterion criterion = Criteria.matchIPv6ExthdrFlags(exthdrFlags);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("exthdrFlags").asInt(), is(exthdrFlags));
+
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -434,8 +405,7 @@
     public void matchLambdaTest() {
         Criterion criterion = Criteria.matchLambda((short) 40000);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("lambda").asInt(), is(40000));
+        assertThat(result, matchesCriterion(criterion));
     }
 
     /**
@@ -445,8 +415,7 @@
     public void matchOpticalSignalTypeTest() {
         Criterion criterion = Criteria.matchOpticalSignalType((byte) 250);
         ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.get("signalType").asInt(), is(250));
+        assertThat(result, matchesCriterion(criterion));
     }
 
 }
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java b/web/api/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java
index 0ffeed4..3c5f119 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java
@@ -25,18 +25,442 @@
 /**
  * Hamcrest matcher for criterion objects.
  */
-public final class CriterionJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+public final class CriterionJsonMatcher extends
+        TypeSafeDiagnosingMatcher<JsonNode> {
 
     final Criterion criterion;
+    Description description;
+    JsonNode jsonCriterion;
 
+    /**
+     * Constructs a matcher object.
+     *
+     * @param criterionValue criterion to match
+     */
     private CriterionJsonMatcher(Criterion criterionValue) {
         criterion = criterionValue;
     }
 
-    // CHECKSTYLE IGNORE MethodLength FOR NEXT 300 LINES
+    /**
+     * Factory to allocate an criterion matcher.
+     *
+     * @param criterion criterion object we are looking for
+     * @return matcher
+     */
+    public static CriterionJsonMatcher matchesCriterion(Criterion criterion) {
+        return new CriterionJsonMatcher(criterion);
+    }
+
+    /**
+     * Matches a port criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.PortCriterion criterion) {
+        final long port = criterion.port().toLong();
+        final long jsonPort = jsonCriterion.get("port").asLong();
+        if (port != jsonPort) {
+            description.appendText("port was " + Long.toString(jsonPort));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches a metadata criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.MetadataCriterion criterion) {
+        final long metadata = criterion.metadata();
+        final long jsonMetadata = jsonCriterion.get("metadata").asLong();
+        if (metadata != jsonMetadata) {
+            description.appendText("metadata was "
+                    + Long.toString(jsonMetadata));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an eth criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.EthCriterion criterion) {
+        final String mac = criterion.mac().toString();
+        final String jsonMac = jsonCriterion.get("mac").textValue();
+        if (!mac.equals(jsonMac)) {
+            description.appendText("mac was " + jsonMac);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an eth type criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.EthTypeCriterion criterion) {
+        final int ethType = criterion.ethType();
+        final int jsonEthType = jsonCriterion.get("ethType").intValue();
+        if (ethType != jsonEthType) {
+            description.appendText("ethType was "
+                    + Integer.toString(jsonEthType));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches a VLAN ID criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.VlanIdCriterion criterion) {
+        final short vlanId = criterion.vlanId().toShort();
+        final short jsonVlanId = jsonCriterion.get("vlanId").shortValue();
+        if (vlanId != jsonVlanId) {
+            description.appendText("vlanId was " + Short.toString(jsonVlanId));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches a VLAN PCP criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.VlanPcpCriterion criterion) {
+        final byte priority = criterion.priority();
+        final byte jsonPriority =
+                (byte) jsonCriterion.get("priority").shortValue();
+        if (priority != jsonPriority) {
+            description.appendText("priority was " + Byte.toString(jsonPriority));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IP DSCP criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IPDscpCriterion criterion) {
+        final byte ipDscp = criterion.ipDscp();
+        final byte jsonIpDscp = (byte) jsonCriterion.get("ipDscp").shortValue();
+        if (ipDscp != jsonIpDscp) {
+            description.appendText("IP DSCP was " + Byte.toString(jsonIpDscp));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IP ECN criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IPEcnCriterion criterion) {
+        final byte ipEcn = criterion.ipEcn();
+        final byte jsonIpEcn = (byte) jsonCriterion.get("ipEcn").shortValue();
+        if (ipEcn != jsonIpEcn) {
+            description.appendText("IP ECN was " + Byte.toString(jsonIpEcn));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IP protocol criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IPProtocolCriterion criterion) {
+        final short protocol = criterion.protocol();
+        final short jsonProtocol = jsonCriterion.get("protocol").shortValue();
+        if (protocol != jsonProtocol) {
+            description.appendText("protocol was "
+                    + Short.toString(jsonProtocol));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IP address criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IPCriterion criterion) {
+        final String ip = criterion.ip().toString();
+        final String jsonIp = jsonCriterion.get("ip").textValue();
+        if (!ip.equals(jsonIp)) {
+            description.appendText("ip was " + jsonIp);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches a TCP port criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.TcpPortCriterion criterion) {
+        final int tcpPort = criterion.tcpPort();
+        final int jsonTcpPort = jsonCriterion.get("tcpPort").intValue();
+        if (tcpPort != jsonTcpPort) {
+            description.appendText("tcp port was "
+                    + Integer.toString(jsonTcpPort));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches a UDP port criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.UdpPortCriterion criterion) {
+        final int udpPort = criterion.udpPort();
+        final int jsonUdpPort = jsonCriterion.get("udpPort").intValue();
+        if (udpPort != jsonUdpPort) {
+            description.appendText("udp port was "
+                    + Integer.toString(jsonUdpPort));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an SCTP port criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.SctpPortCriterion criterion) {
+        final int sctpPort = criterion.sctpPort();
+        final int jsonSctpPort = jsonCriterion.get("sctpPort").intValue();
+        if (sctpPort != jsonSctpPort) {
+            description.appendText("sctp port was "
+                    + Integer.toString(jsonSctpPort));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an ICMP type criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IcmpTypeCriterion criterion) {
+        final short icmpType = criterion.icmpType();
+        final short jsonIcmpType = jsonCriterion.get("icmpType").shortValue();
+        if (icmpType != jsonIcmpType) {
+            description.appendText("icmp type was "
+                    + Short.toString(jsonIcmpType));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an ICMP code criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IcmpCodeCriterion criterion) {
+        final short icmpCode = criterion.icmpCode();
+        final short jsonIcmpCode = jsonCriterion.get("icmpCode").shortValue();
+        if (icmpCode != jsonIcmpCode) {
+            description.appendText("icmp code was "
+                    + Short.toString(jsonIcmpCode));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IPV6 flow label criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IPv6FlowLabelCriterion criterion) {
+        final int flowLabel = criterion.flowLabel();
+        final int jsonFlowLabel = jsonCriterion.get("flowLabel").intValue();
+        if (flowLabel != jsonFlowLabel) {
+            description.appendText("IPv6 flow label was "
+                    + Integer.toString(jsonFlowLabel));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an ICMP V6 type criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.Icmpv6TypeCriterion criterion) {
+        final short icmpv6Type = criterion.icmpv6Type();
+        final short jsonIcmpv6Type =
+                jsonCriterion.get("icmpv6Type").shortValue();
+        if (icmpv6Type != jsonIcmpv6Type) {
+            description.appendText("icmpv6 type was "
+                    + Short.toString(jsonIcmpv6Type));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IPV6 code criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.Icmpv6CodeCriterion criterion) {
+        final short icmpv6Code = criterion.icmpv6Code();
+        final short jsonIcmpv6Code =
+                jsonCriterion.get("icmpv6Code").shortValue();
+        if (icmpv6Code != jsonIcmpv6Code) {
+            description.appendText("icmpv6 code was "
+                    + Short.toString(jsonIcmpv6Code));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IPV6 ND target criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IPv6NDTargetAddressCriterion criterion) {
+        final String targetAddress =
+                criterion.targetAddress().toString();
+        final String jsonTargetAddress =
+                jsonCriterion.get("targetAddress").textValue();
+        if (!targetAddress.equals(jsonTargetAddress)) {
+            description.appendText("target address was " +
+                    jsonTargetAddress);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IPV6 ND link layer criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IPv6NDLinkLayerAddressCriterion criterion) {
+        final String llAddress =
+                criterion.mac().toString();
+        final String jsonLlAddress =
+                jsonCriterion.get("mac").textValue();
+        if (!llAddress.equals(jsonLlAddress)) {
+            description.appendText("mac was " + jsonLlAddress);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an MPLS label criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.MplsCriterion criterion) {
+        final int label = criterion.label();
+        final int jsonLabel = jsonCriterion.get("label").intValue();
+        if (label != jsonLabel) {
+            description.appendText("label was " + Integer.toString(jsonLabel));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an IPV6 exthdr criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.IPv6ExthdrFlagsCriterion criterion) {
+        final int exthdrFlags = criterion.exthdrFlags();
+        final int jsonExthdrFlags =
+                jsonCriterion.get("exthdrFlags").intValue();
+        if (exthdrFlags != jsonExthdrFlags) {
+            description.appendText("exthdrFlags was "
+                    + Long.toHexString(jsonExthdrFlags));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches a lambda criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.LambdaCriterion criterion) {
+        final int lambda = criterion.lambda();
+        final int jsonLambda = jsonCriterion.get("lambda").intValue();
+        if (lambda != jsonLambda) {
+            description.appendText("lambda was " + Integer.toString(lambda));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Matches an optical signal type criterion object.
+     *
+     * @param criterion criterion to match
+     * @return true if the JSON matches the criterion, false otherwise.
+     */
+    private boolean matchCriterion(Criteria.OpticalSignalTypeCriterion criterion) {
+        final short signalType = criterion.signalType();
+        final short jsonSignalType = jsonCriterion.get("signalType").shortValue();
+        if (signalType != jsonSignalType) {
+            description.appendText("signal type was " + Short.toString(signalType));
+            return false;
+        }
+        return true;
+    }
+
     @Override
     public boolean matchesSafely(JsonNode jsonCriterion,
                                  Description description) {
+        this.description = description;
+        this.jsonCriterion = jsonCriterion;
         final String type = criterion.type().name();
         final String jsonType = jsonCriterion.get("type").asText();
         if (!type.equals(jsonType)) {
@@ -48,285 +472,88 @@
 
             case IN_PORT:
             case IN_PHY_PORT:
-                final Criteria.PortCriterion portCriterion =
-                        (Criteria.PortCriterion) criterion;
-                final long port = portCriterion.port().toLong();
-                final long jsonPort = jsonCriterion.get("port").asLong();
-                if (port != jsonPort) {
-                    description.appendText("port was " + Long.toString(jsonPort));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.PortCriterion) criterion);
 
             case METADATA:
-                final Criteria.MetadataCriterion metadataCriterion =
-                        (Criteria.MetadataCriterion) criterion;
-                final long metadata = metadataCriterion.metadata();
-                final long jsonMetadata = jsonCriterion.get("metadata").asLong();
-                if (metadata != jsonMetadata) {
-                    description.appendText("metadata was " + Long.toString(jsonMetadata));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.MetadataCriterion) criterion);
 
             case ETH_DST:
             case ETH_SRC:
-                final Criteria.EthCriterion ethCriterion =
-                    (Criteria.EthCriterion) criterion;
-                final String mac = ethCriterion.mac().toString();
-                final String jsonMac = jsonCriterion.get("mac").textValue();
-                if (!mac.equals(jsonMac)) {
-                    description.appendText("mac was " + jsonMac);
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.EthCriterion) criterion);
 
             case ETH_TYPE:
-                final Criteria.EthTypeCriterion ethTypeCriterion =
-                        (Criteria.EthTypeCriterion) criterion;
-                final String ethType =
-                    Long.toHexString(ethTypeCriterion.ethType());
-                final String jsonEthType = jsonCriterion.get("ethType").textValue();
-                if (!ethType.equals(jsonEthType)) {
-                    description.appendText("ethType was " + jsonEthType);
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.EthTypeCriterion) criterion);
 
             case VLAN_VID:
-                final Criteria.VlanIdCriterion vlanIdCriterion =
-                        (Criteria.VlanIdCriterion) criterion;
-                final short vlanId = vlanIdCriterion.vlanId().toShort();
-                final short jsonVlanId = jsonCriterion.get("vlanId").shortValue();
-                if (vlanId != jsonVlanId) {
-                    description.appendText("vlanId was " + Short.toString(jsonVlanId));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.VlanIdCriterion) criterion);
 
             case VLAN_PCP:
-                final Criteria.VlanPcpCriterion vlanPcpCriterion =
-                        (Criteria.VlanPcpCriterion) criterion;
-                final byte priority = vlanPcpCriterion.priority();
-                final byte jsonPriority = (byte) jsonCriterion.get("priority").shortValue();
-                if (priority != jsonPriority) {
-                    description.appendText("priority was " + Byte.toString(jsonPriority));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.VlanPcpCriterion) criterion);
 
             case IP_DSCP:
-                final Criteria.IPDscpCriterion ipDscpCriterion =
-                        (Criteria.IPDscpCriterion) criterion;
-                final byte ipDscp = ipDscpCriterion.ipDscp();
-                final byte jsonIpDscp = (byte) jsonCriterion.get("ipDscp").shortValue();
-                if (ipDscp != jsonIpDscp) {
-                    description.appendText("IP DSCP was " + Byte.toString(jsonIpDscp));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.IPDscpCriterion) criterion);
 
             case IP_ECN:
-                final Criteria.IPEcnCriterion ipEcnCriterion =
-                        (Criteria.IPEcnCriterion) criterion;
-                final byte ipEcn = ipEcnCriterion.ipEcn();
-                final byte jsonIpEcn = (byte) jsonCriterion.get("ipEcn").shortValue();
-                if (ipEcn != jsonIpEcn) {
-                    description.appendText("IP ECN was " + Byte.toString(jsonIpEcn));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.IPEcnCriterion) criterion);
 
             case IP_PROTO:
-                final Criteria.IPProtocolCriterion iPProtocolCriterion =
-                        (Criteria.IPProtocolCriterion) criterion;
-                final short protocol = iPProtocolCriterion.protocol();
-                final short jsonProtocol = jsonCriterion.get("protocol").shortValue();
-                if (protocol != jsonProtocol) {
-                    description.appendText("protocol was " + Short.toString(jsonProtocol));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.IPProtocolCriterion) criterion);
 
             case IPV4_SRC:
             case IPV4_DST:
             case IPV6_SRC:
             case IPV6_DST:
-                final Criteria.IPCriterion ipCriterion =
-                    (Criteria.IPCriterion) criterion;
-                final String ip = ipCriterion.ip().toString();
-                final String jsonIp = jsonCriterion.get("ip").textValue();
-                if (!ip.equals(jsonIp)) {
-                    description.appendText("ip was " + jsonIp);
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.IPCriterion) criterion);
 
             case TCP_SRC:
             case TCP_DST:
-                final Criteria.TcpPortCriterion tcpPortCriterion =
-                        (Criteria.TcpPortCriterion) criterion;
-                final int tcpPort = tcpPortCriterion.tcpPort();
-                final int jsonTcpPort = jsonCriterion.get("tcpPort").intValue();
-                if (tcpPort != jsonTcpPort) {
-                    description.appendText("tcp port was " + Integer.toString(jsonTcpPort));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.TcpPortCriterion) criterion);
 
             case UDP_SRC:
             case UDP_DST:
-                final Criteria.UdpPortCriterion udpPortCriterion =
-                        (Criteria.UdpPortCriterion) criterion;
-                final int udpPort = udpPortCriterion.udpPort();
-                final int jsonUdpPort = jsonCriterion.get("udpPort").intValue();
-                if (udpPort != jsonUdpPort) {
-                    description.appendText("udp port was " + Integer.toString(jsonUdpPort));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.UdpPortCriterion) criterion);
 
             case SCTP_SRC:
             case SCTP_DST:
-                final Criteria.SctpPortCriterion sctpPortCriterion =
-                        (Criteria.SctpPortCriterion) criterion;
-                final int sctpPort = sctpPortCriterion.sctpPort();
-                final int jsonSctpPort = jsonCriterion.get("sctpPort").intValue();
-                if (sctpPort != jsonSctpPort) {
-                    description.appendText("sctp port was " + Integer.toString(jsonSctpPort));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.SctpPortCriterion) criterion);
 
             case ICMPV4_TYPE:
-                final Criteria.IcmpTypeCriterion icmpTypeCriterion =
-                        (Criteria.IcmpTypeCriterion) criterion;
-                final short icmpType = icmpTypeCriterion.icmpType();
-                final short jsonIcmpType = jsonCriterion.get("icmpType").shortValue();
-                if (icmpType != jsonIcmpType) {
-                    description.appendText("icmp type was " + Short.toString(jsonIcmpType));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.IcmpTypeCriterion) criterion);
 
             case ICMPV4_CODE:
-                final Criteria.IcmpCodeCriterion icmpCodeCriterion =
-                        (Criteria.IcmpCodeCriterion) criterion;
-                final short icmpCode = icmpCodeCriterion.icmpCode();
-                final short jsonIcmpCode = jsonCriterion.get("icmpCode").shortValue();
-                if (icmpCode != jsonIcmpCode) {
-                    description.appendText("icmp code was " + Short.toString(jsonIcmpCode));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.IcmpCodeCriterion) criterion);
 
             case IPV6_FLABEL:
-                final Criteria.IPv6FlowLabelCriterion ipv6FlowLabelCriterion =
-                        (Criteria.IPv6FlowLabelCriterion) criterion;
-                final int flowLabel = ipv6FlowLabelCriterion.flowLabel();
-                final int jsonFlowLabel = jsonCriterion.get("flowLabel").intValue();
-                if (flowLabel != jsonFlowLabel) {
-                    description.appendText("IPv6 flow label was " + Integer.toString(jsonFlowLabel));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.IPv6FlowLabelCriterion) criterion);
 
             case ICMPV6_TYPE:
-                final Criteria.Icmpv6TypeCriterion icmpv6TypeCriterion =
-                        (Criteria.Icmpv6TypeCriterion) criterion;
-                final short icmpv6Type = icmpv6TypeCriterion.icmpv6Type();
-                final short jsonIcmpv6Type = jsonCriterion.get("icmpv6Type").shortValue();
-                if (icmpv6Type != jsonIcmpv6Type) {
-                    description.appendText("icmpv6 type was " + Short.toString(jsonIcmpv6Type));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.Icmpv6TypeCriterion) criterion);
 
             case ICMPV6_CODE:
-                final Criteria.Icmpv6CodeCriterion icmpv6CodeCriterion =
-                        (Criteria.Icmpv6CodeCriterion) criterion;
-                final short icmpv6Code = icmpv6CodeCriterion.icmpv6Code();
-                final short jsonIcmpv6Code = jsonCriterion.get("icmpv6Code").shortValue();
-                if (icmpv6Code != jsonIcmpv6Code) {
-                    description.appendText("icmpv6 code was " + Short.toString(jsonIcmpv6Code));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.Icmpv6CodeCriterion) criterion);
 
             case IPV6_ND_TARGET:
-                final Criteria.IPv6NDTargetAddressCriterion
-                    ipv6NDTargetAddressCriterion =
-                        (Criteria.IPv6NDTargetAddressCriterion) criterion;
-                final String targetAddress =
-                    ipv6NDTargetAddressCriterion.targetAddress().toString();
-                final String jsonTargetAddress =
-                    jsonCriterion.get("targetAddress").textValue();
-                if (!targetAddress.equals(jsonTargetAddress)) {
-                    description.appendText("target address was " +
-                                           jsonTargetAddress);
-                    return false;
-                }
-                break;
+                return matchCriterion(
+                        (Criteria.IPv6NDTargetAddressCriterion) criterion);
 
             case IPV6_ND_SLL:
             case IPV6_ND_TLL:
-                final Criteria.IPv6NDLinkLayerAddressCriterion
-                    ipv6NDLinkLayerAddressCriterion =
-                        (Criteria.IPv6NDLinkLayerAddressCriterion) criterion;
-                final String llAddress =
-                    ipv6NDLinkLayerAddressCriterion.mac().toString();
-                final String jsonLlAddress =
-                    jsonCriterion.get("mac").textValue();
-                if (!llAddress.equals(jsonLlAddress)) {
-                    description.appendText("mac was " + jsonLlAddress);
-                    return false;
-                }
-                break;
+                return matchCriterion(
+                        (Criteria.IPv6NDLinkLayerAddressCriterion) criterion);
 
             case MPLS_LABEL:
-                final Criteria.MplsCriterion mplsCriterion =
-                        (Criteria.MplsCriterion) criterion;
-                final int label = mplsCriterion.label();
-                final int jsonLabel = jsonCriterion.get("label").intValue();
-                if (label != jsonLabel) {
-                    description.appendText("label was " + Integer.toString(jsonLabel));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.MplsCriterion) criterion);
 
             case IPV6_EXTHDR:
-                final Criteria.IPv6ExthdrFlagsCriterion exthdrCriterion =
-                        (Criteria.IPv6ExthdrFlagsCriterion) criterion;
-                final int exthdrFlags = exthdrCriterion.exthdrFlags();
-                final int jsonExthdrFlags =
-                    jsonCriterion.get("exthdrFlags").intValue();
-                if (exthdrFlags != jsonExthdrFlags) {
-                    description.appendText("exthdrFlags was " + Long.toHexString(jsonExthdrFlags));
-                    return false;
-                }
-                break;
+                return matchCriterion(
+                        (Criteria.IPv6ExthdrFlagsCriterion) criterion);
 
             case OCH_SIGID:
-                final Criteria.LambdaCriterion lambdaCriterion =
-                        (Criteria.LambdaCriterion) criterion;
-                final int lambda = lambdaCriterion.lambda();
-                final int jsonLambda = jsonCriterion.get("lambda").intValue();
-                if (lambda != jsonLambda) {
-                    description.appendText("lambda was " + Integer.toString(lambda));
-                    return false;
-                }
-                break;
+                return matchCriterion((Criteria.LambdaCriterion) criterion);
 
             case OCH_SIGTYPE:
-                final Criteria.OpticalSignalTypeCriterion opticalSignalTypeCriterion =
-                        (Criteria.OpticalSignalTypeCriterion) criterion;
-                final short signalType = opticalSignalTypeCriterion.signalType();
-                final short jsonSignalType = jsonCriterion.get("signalType").shortValue();
-                if (signalType != jsonSignalType) {
-                    description.appendText("signal type was " + Short.toString(signalType));
-                    return false;
-                }
-                break;
+                return matchCriterion(
+                        (Criteria.OpticalSignalTypeCriterion) criterion);
 
             default:
                 // Don't know how to format this type
@@ -334,21 +561,10 @@
                         criterion.type());
                 return false;
         }
-        return true;
     }
 
     @Override
     public void describeTo(Description description) {
         description.appendText(criterion.toString());
     }
-
-    /**
-     * Factory to allocate an criterion matcher.
-     *
-     * @param criterion criterion object we are looking for
-     * @return matcher
-     */
-    public static CriterionJsonMatcher matchesCriterion(Criterion criterion) {
-        return new CriterionJsonMatcher(criterion);
-    }
 }