Merge into master from pull request #91:
java_gen/DataPathId: make comparable (https://github.com/floodlight/loxigen/pull/91)
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java
index 87be7c2..009dae5 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/MacAddress.java
@@ -19,6 +19,9 @@
     private final static long NONE_VAL = 0x0L;
     public static final MacAddress NONE = new MacAddress(NONE_VAL);
 
+    private final static long BROADCAST_VAL = 0x0000FFFFFFFFFFFFL;
+    public static final MacAddress BROADCAST = new MacAddress(BROADCAST_VAL);
+
     public static final MacAddress NO_MASK = MacAddress.of(0xFFFFFFFFFFFFFFFFl);
     public static final MacAddress FULL_MASK = MacAddress.of(0x0);
 
@@ -27,6 +30,9 @@
     }
 
     public static MacAddress of(final byte[] address) {
+        if (address.length != MacAddrLen)
+            throw new IllegalArgumentException(
+                    "Mac address byte array must be exactly 6 bytes long; length = " + address.length);
         long raw =
                 (address[0] & 0xFFL) << 40 | (address[1] & 0xFFL) << 32
                         | (address[2] & 0xFFL) << 24 | (address[3] & 0xFFL) << 16
@@ -34,30 +40,36 @@
         return MacAddress.of(raw);
     }
 
-    public static MacAddress of(final long raw) {
+    public static MacAddress of(long raw) {
+        raw &= BROADCAST_VAL;
         if(raw == NONE_VAL)
             return NONE;
-
+        if (raw == BROADCAST_VAL)
+            return BROADCAST;
         return new MacAddress(raw);
     }
 
     public static MacAddress of(final String string) {
         int index = 0;
         int shift = 40;
+        final String FORMAT_ERROR = "Mac address is not well-formed. " +
+                "It must consist of 6 hex digit pairs separated by colons: ";
 
         long raw = 0;
         if (string.length() != 6 * 2 + 5)
-            throw new IllegalArgumentException("Mac address not well formed: " + string);
+            throw new IllegalArgumentException(FORMAT_ERROR + string);
 
         while (shift >= 0) {
-            raw |=
-                    ((long) (Character.digit(string.charAt(index++), 16) << 4 | Character
-                            .digit(string.charAt(index++), 16))) << shift;
+            int digit1 = Character.digit(string.charAt(index++), 16);
+            int digit2 = Character.digit(string.charAt(index++), 16);
+            if ((digit1 < 0) || (digit2 < 0))
+                throw new IllegalArgumentException(FORMAT_ERROR + string);
+            raw |= ((long) (digit1 << 4 | digit2)) << shift;
 
             if (shift == 0)
                 break;
             if (string.charAt(index++) != ':')
-                throw new IllegalArgumentException("Mac address not well formed: " + string);
+                throw new IllegalArgumentException(FORMAT_ERROR + string);
             shift -= 8;
         }
         return MacAddress.of(raw);
@@ -82,6 +94,25 @@
         return bytesCache;
     }
 
+    /**
+     * Returns {@code true} if the MAC address is the broadcast address.
+     * @return {@code true} if the MAC address is the broadcast address.
+     */
+    public boolean isBroadcast() {
+        return this == BROADCAST;
+    }
+
+    /**
+     * Returns {@code true} if the MAC address is a multicast address.
+     * @return {@code true} if the MAC address is a multicast address.
+     */
+    public boolean isMulticast() {
+        if (isBroadcast()) {
+            return false;
+        }
+        return (rawValue & (0x01L << 40)) != 0;
+    }
+
     @Override
     public int getLength() {
         return MacAddrLen;
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBufferId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBufferId.java
index f541b61..f3812dd 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBufferId.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBufferId.java
@@ -1,8 +1,6 @@
 package org.projectfloodlight.openflow.types;
 
-import org.jboss.netty.buffer.ChannelBuffer;
 import org.projectfloodlight.openflow.annotations.Immutable;
-import org.projectfloodlight.openflow.exceptions.OFParseError;
 
 import com.google.common.primitives.UnsignedInts;
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
index ea2c4a9..13cfdc7 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/ChannelUtils.java
@@ -21,7 +21,12 @@
     public static String readFixedLengthString(ChannelBuffer bb, int length) {
         byte[] dst = new byte[length];
         bb.readBytes(dst, 0, length);
-        return new String(dst, Charsets.US_ASCII);
+        int validLength = 0;
+        for (validLength = 0; validLength < length; validLength++) {
+            if (dst[validLength] == 0)
+                break;
+        }
+        return new String(dst, 0, validLength, Charsets.US_ASCII);
     }
 
     public static void writeFixedLengthString(ChannelBuffer bb, String string,
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java
index dc25b12..ed380ee 100644
--- a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/MacAddressTest.java
@@ -2,6 +2,8 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.util.Arrays;
@@ -29,9 +31,11 @@
             0x00ffffffffffffL
     };
 
-    String[] invalidMacs = {
+    String[] invalidMacStrings = {
             "",
             "1.2.3.4",
+            "0T:00:01:02:03:04",
+            "00:01:02:03:04:05:06",
             "00:ff:ef:12:12:ff:",
             "00:fff:ef:12:12:ff",
             "01:02:03:04:05;06",
@@ -39,6 +43,10 @@
             "01:02:03:04"
     };
 
+    byte[][] invalidMacBytes = {
+            new byte[]{0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
+            new byte[]{0x01, 0x01, 0x02, 0x03, 0x04}
+    };
 
     @Test
     public void testOfString() {
@@ -72,8 +80,8 @@
 
 
     @Test
-    public void testInvalidMacss() throws OFParseError {
-        for(String invalid : invalidMacs) {
+    public void testInvalidMacStrings() throws OFParseError {
+        for(String invalid : invalidMacStrings) {
             try {
                 MacAddress.of(invalid);
                 fail("Invalid IP "+invalid+ " should have raised IllegalArgumentException");
@@ -82,4 +90,54 @@
             }
         }
     }
+
+    @Test
+    public void testInvalidMacBytes() throws OFParseError {
+        for(byte[] invalid : invalidMacBytes) {
+            try {
+                MacAddress.of(invalid);
+                fail("Invalid IP "+invalid+ " should have raised IllegalArgumentException");
+            } catch(IllegalArgumentException e) {
+                // ok
+            }
+        }
+    }
+
+    //  Test data is imported from org.projectfloodlight.packet.EthernetTest
+    @Test
+    public void testToLong() {
+        assertEquals(
+                281474976710655L,
+                MacAddress.of(new byte[]{(byte) 0xff, (byte) 0xff,
+                        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}).getLong());
+
+        assertEquals(
+                1103823438081L,
+                MacAddress.of(new byte[] { (byte) 0x01, (byte) 0x01,
+                        (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01 }).getLong());
+
+        assertEquals(
+                141289400074368L,
+                MacAddress.of(new byte[] { (byte) 0x80, (byte) 0x80,
+                        (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80 }).getLong());
+
+    }
+
+    @Test
+    public void testIsBroadcast() {
+        assertTrue(MacAddress.of("FF:FF:FF:FF:FF:FF").isBroadcast());
+        assertTrue(MacAddress.of(-1).isBroadcast());
+        assertTrue(MacAddress.of(0x05FFFFFFFFFFFFL).isBroadcast());
+        assertFalse(MacAddress.of("11:22:33:44:55:66").isBroadcast());
+    }
+
+    @Test
+    public void testIsMulticast() {
+        assertTrue(MacAddress.of("01:80:C2:00:00:00").isMulticast());
+        assertFalse(MacAddress.of("00:80:C2:00:00:00").isMulticast());
+        assertFalse(MacAddress.of("FE:80:C2:00:00:00").isMulticast());
+        assertFalse(MacAddress.of(-1).isMulticast());
+        assertFalse(MacAddress.of(0x05FFFFFFFFFFFFL).isMulticast());
+        assertFalse(MacAddress.of("FF:FF:FF:FF:FF:FF").isMulticast());
+    }
 }
diff --git a/java_gen/templates/custom/OFMatchV1Ver10.Builder.java b/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
index 38cc127..239828e 100644
--- a/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
+++ b/java_gen/templates/custom/OFMatchV1Ver10.Builder.java
@@ -25,6 +25,15 @@
                 case VLAN_PCP:
                     result = vlanPcp;
                     break;
+                case ARP_OP:
+                    result = ArpOpcode.of(ipProto.getIpProtocolNumber());
+                    break;
+                case ARP_SPA:
+                    result = ipv4Src;
+                    break;
+                case ARP_TPA:
+                    result = ipv4Dst;
+                    break;
                 case IP_DSCP:
                     result = ipDscp;
                     break;
@@ -32,13 +41,13 @@
                     result = ipProto;
                     break;
                 case IPV4_SRC:
-                    result = ipv4Dst;
+                    result = ipv4Src;
                     break;
                 case IPV4_DST:
                     result = ipv4Dst;
                     break;
                 case TCP_SRC:
-                    result = ipv4Src;
+                    result = tcpSrc;
                     break;
                 case TCP_DST:
                     result = tcpDst;
@@ -77,10 +86,12 @@
             Object result;
             switch (field.id) {
                 case IPV4_SRC:
+                case ARP_SPA:
                     int srcBitMask = (-1) << (32 - getIpv4SrcCidrMaskLen());
                     result = IPv4AddressWithMask.of(ipv4Src, IPv4Address.of(srcBitMask));
                     break;
                 case IPV4_DST:
+                case ARP_TPA:
                     int dstMaskedBits = Math.min(32, (wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT);
                     int dstBitMask = (-1) << (32 - getIpv4DstCidrMaskLen());
 
@@ -101,6 +112,9 @@
                 case ETH_TYPE:
                 case VLAN_VID:
                 case VLAN_PCP:
+                case ARP_OP:
+                case ARP_SPA:
+                case ARP_TPA:
                 case IP_DSCP:
                 case IP_PROTO:
                 case IPV4_SRC:
@@ -122,6 +136,8 @@
         @Override
         public boolean supportsMasked(MatchField<?> field) {
             switch (field.id) {
+                case ARP_SPA:
+                case ARP_TPA:
                 case IPV4_SRC:
                 case IPV4_DST:
                     return true;
@@ -145,6 +161,12 @@
                     return (this.wildcards & OFPFW_DL_VLAN) == 0;
                 case VLAN_PCP:
                     return (this.wildcards & OFPFW_DL_VLAN_PCP) == 0;
+                case ARP_OP:
+                    return (this.wildcards & OFPFW_NW_PROTO) == 0;
+                case ARP_SPA:
+                    return this.getIpv4SrcCidrMaskLen() >= 32;
+                case ARP_TPA:
+                    return this.getIpv4DstCidrMaskLen() >= 32;
                 case IP_DSCP:
                     return (this.wildcards & OFPFW_NW_TOS) == 0;
                 case IP_PROTO:
@@ -216,6 +238,12 @@
                     return (this.wildcards & OFPFW_DL_VLAN) != 0;
                 case VLAN_PCP:
                     return (this.wildcards & OFPFW_DL_VLAN_PCP) != 0;
+                case ARP_OP:
+                    return (this.wildcards & OFPFW_NW_PROTO) != 0;
+                case ARP_SPA:
+                    return this.getIpv4SrcCidrMaskLen() <= 0;
+                case ARP_TPA:
+                    return this.getIpv4DstCidrMaskLen() <= 0;
                 case IP_DSCP:
                     return (this.wildcards & OFPFW_NW_TOS) != 0;
                 case IP_PROTO:
@@ -248,11 +276,13 @@
         @Override
         public boolean isPartiallyMasked(MatchField<?> field) {
             switch (field.id) {
+                case ARP_SPA:
                 case IPV4_SRC:
                     int srcCidrLen = getIpv4SrcCidrMaskLen();
                     return srcCidrLen > 0 && srcCidrLen < 32;
+                case ARP_TPA:
                 case IPV4_DST:
-                    int dstCidrLen = getIpv4SrcCidrMaskLen();
+                    int dstCidrLen = getIpv4DstCidrMaskLen();
                     return dstCidrLen > 0 && dstCidrLen < 32;
                 default:
                     throw new UnsupportedOperationException("OFMatch does not support masked matching on field " + field.getName());
@@ -300,10 +330,16 @@
                     setInPort((OFPort) value);
                     wildcards &= ~OFPFW_IN_PORT;
                     break;
+                case ARP_OP:
+                    setIpProto(IpProtocol.of((short)((ArpOpcode)value).getOpcode()));
+                    wildcards &= ~OFPFW_NW_PROTO;
+                    break;
+                case ARP_TPA:
                 case IPV4_DST:
                     setIpv4Dst((IPv4Address) value);
                     wildcards &= ~OFPFW_NW_DST_MASK;
                     break;
+                case ARP_SPA:
                 case IPV4_SRC:
                     setIpv4Src((IPv4Address) value);
                     wildcards &= ~OFPFW_NW_SRC_MASK;
@@ -347,6 +383,7 @@
                 case VLAN_VID:
                     setVlanVid((VlanVid) value);
                     wildcards &= ~OFPFW_DL_VLAN;
+                    break;
                 default:
                     throw new UnsupportedOperationException(
                             "OFMatch does not support matching on field " + field.getName());
@@ -359,6 +396,8 @@
                 F value, F mask) {
             initWildcards();
             switch (field.id) {
+                case ARP_SPA:
+                case ARP_TPA:
                 case IPV4_DST:
                 case IPV4_SRC:
                     Object valObj = value;
@@ -369,10 +408,12 @@
                         throw new UnsupportedOperationException("OFMatch only supports CIDR masks for IPv4");
                     int maskLen = 32 - Integer.bitCount(maskval);
                     switch(field.id) {
+                        case ARP_TPA:
                         case IPV4_DST:
                             setIpv4Dst(ip);
                             wildcards = (wildcards &~OFPFW_NW_DST_MASK) | (maskLen << OFPFW_NW_DST_SHIFT);
                             break;
+                        case ARP_SPA:
                         case IPV4_SRC:
                             setIpv4Src(ip);
                             wildcards = (wildcards &~OFPFW_NW_SRC_MASK) | (maskLen << OFPFW_NW_SRC_SHIFT);
@@ -428,10 +469,12 @@
                     setInPort(OFPort.of(0)); // NOTE: not 'NONE' -- that is 0xFF for ports
                     wildcards |= OFPFW_IN_PORT;
                     break;
+                case ARP_TPA:
                 case IPV4_DST:
                     setIpv4Dst(IPv4Address.NONE);
                     wildcards |= OFPFW_NW_DST_MASK;
                     break;
+                case ARP_SPA:
                 case IPV4_SRC:
                     setIpv4Src(IPv4Address.NONE);
                     wildcards |= OFPFW_NW_SRC_MASK;
diff --git a/java_gen/templates/custom/OFMatchV1Ver10.java b/java_gen/templates/custom/OFMatchV1Ver10.java
index eaaacb0..82ff10f 100644
--- a/java_gen/templates/custom/OFMatchV1Ver10.java
+++ b/java_gen/templates/custom/OFMatchV1Ver10.java
@@ -61,6 +61,15 @@
             case VLAN_PCP:
                 result = vlanPcp;
                 break;
+            case ARP_OP:
+                result = ArpOpcode.of(ipProto.getIpProtocolNumber());
+                break;
+            case ARP_SPA:
+                result = ipv4Src;
+                break;
+            case ARP_TPA:
+                result = ipv4Dst;
+                break;
             case IP_DSCP:
                 result = ipDscp;
                 break;
@@ -68,7 +77,7 @@
                 result = ipProto;
                 break;
             case IPV4_SRC:
-                result = ipv4Dst;
+                result = ipv4Src;
                 break;
             case IPV4_DST:
                 result = ipv4Dst;
@@ -114,10 +123,12 @@
             return null;
         Object result;
         switch (field.id) {
+            case ARP_SPA:
             case IPV4_SRC:
                 int srcBitMask = (-1) << (32 - getIpv4SrcCidrMaskLen());
                 result = IPv4AddressWithMask.of(ipv4Src, IPv4Address.of(srcBitMask));
                 break;
+            case ARP_TPA:
             case IPV4_DST:
                 int dstBitMask = (-1) << (32 - getIpv4DstCidrMaskLen());
 
@@ -138,6 +149,9 @@
             case ETH_TYPE:
             case VLAN_VID:
             case VLAN_PCP:
+            case ARP_OP:
+            case ARP_SPA:
+            case ARP_TPA:
             case IP_DSCP:
             case IP_PROTO:
             case IPV4_SRC:
@@ -159,6 +173,8 @@
     @Override
     public boolean supportsMasked(MatchField<?> field) {
         switch (field.id) {
+            case ARP_SPA:
+            case ARP_TPA:
             case IPV4_SRC:
             case IPV4_DST:
                 return true;
@@ -185,6 +201,12 @@
                 return (this.wildcards & OFPFW_DL_VLAN) == 0;
             case VLAN_PCP:
                 return (this.wildcards & OFPFW_DL_VLAN_PCP) == 0;
+            case ARP_OP:
+                return (this.wildcards & OFPFW_NW_PROTO) == 0;
+            case ARP_SPA:
+                return this.getIpv4SrcCidrMaskLen() >= 32;
+            case ARP_TPA:
+                return this.getIpv4DstCidrMaskLen() >= 32;
             case IP_DSCP:
                 return (this.wildcards & OFPFW_NW_TOS) == 0;
             case IP_PROTO:
@@ -259,6 +281,12 @@
                 return (this.wildcards & OFPFW_DL_VLAN) != 0;
             case VLAN_PCP:
                 return (this.wildcards & OFPFW_DL_VLAN_PCP) != 0;
+            case ARP_OP:
+                return (this.wildcards & OFPFW_NW_PROTO) != 0;
+            case ARP_SPA:
+                return this.getIpv4SrcCidrMaskLen() <= 0;
+            case ARP_TPA:
+                return this.getIpv4DstCidrMaskLen() <= 0;
             case IP_DSCP:
                 return (this.wildcards & OFPFW_NW_TOS) != 0;
             case IP_PROTO:
@@ -294,9 +322,11 @@
             return false;
 
         switch (field.id) {
+            case ARP_SPA:
             case IPV4_SRC:
                 int srcCidrLen = getIpv4SrcCidrMaskLen();
                 return srcCidrLen > 0 && srcCidrLen < 32;
+            case ARP_TPA:
             case IPV4_DST:
                 int dstCidrLen = getIpv4SrcCidrMaskLen();
                 return dstCidrLen > 0 && dstCidrLen < 32;
diff --git a/java_gen/templates/of_factories.java b/java_gen/templates/of_factories.java
index 0044335..0a27c59 100644
--- a/java_gen/templates/of_factories.java
+++ b/java_gen/templates/of_factories.java
@@ -36,6 +36,9 @@
 //:: include("_imports.java")
 
 public final class OFFactories {
+
+    private final static GenericReader GENERIC_READER = new GenericReader();
+
     public static OFFactory getFactory(OFVersion version) {
         switch(version) {
             //:: for v in versions:
@@ -46,4 +49,25 @@
                 throw new IllegalArgumentException("Unknown version: "+version);
             }
     }
+    
+    private static class GenericReader implements OFMessageReader<OFMessage> {
+        public OFMessage readFrom(ChannelBuffer bb) throws OFParseError {
+            short wireVersion = U8.f(bb.getByte(0));
+            OFFactory factory;
+            switch (wireVersion) {
+            //:: for v in versions:
+            case ${v.int_version}:
+                factory = org.projectfloodlight.openflow.protocol.ver${v.of_version}.OFFactoryVer${v.of_version}.INSTANCE;
+                break;
+            //:: #endfor
+            default:
+                throw new IllegalArgumentException("Unknown wire version: " + wireVersion);
+            }
+            return factory.getReader().readFrom(bb);
+        }
+    }
+
+    public static OFMessageReader<OFMessage> getGenericReader() {
+        return GENERIC_READER;
+    }
 }
diff --git a/openflow_input/standard-1.0 b/openflow_input/standard-1.0
index 3493c6b..79d5bad 100644
--- a/openflow_input/standard-1.0
+++ b/openflow_input/standard-1.0
@@ -94,6 +94,7 @@
     OFPPC_NO_FLOOD = 0x10,
     OFPPC_NO_FWD = 0x20,
     OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
 };
 
 enum ofp_port_state(wire_type=uint32_t, bitmask=True) {
diff --git a/openflow_input/standard-1.1 b/openflow_input/standard-1.1
index f985b6d..9937c6d 100644
--- a/openflow_input/standard-1.1
+++ b/openflow_input/standard-1.1
@@ -135,6 +135,7 @@
     OFPPC_NO_RECV = 0x4,
     OFPPC_NO_FWD = 0x20,
     OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
 };
 
 enum ofp_port_state(wire_type=uint32_t, bitmask=True) {
diff --git a/openflow_input/standard-1.2 b/openflow_input/standard-1.2
index 9032458..182794e 100644
--- a/openflow_input/standard-1.2
+++ b/openflow_input/standard-1.2
@@ -138,6 +138,7 @@
     OFPPC_NO_RECV = 0x4,
     OFPPC_NO_FWD = 0x20,
     OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
 };
 
 enum ofp_port_state(wire_type=uint32_t, bitmask=True) {
diff --git a/openflow_input/standard-1.3 b/openflow_input/standard-1.3
index b567d6d..9d53f11 100644
--- a/openflow_input/standard-1.3
+++ b/openflow_input/standard-1.3
@@ -137,6 +137,7 @@
     OFPPC_NO_RECV = 0x4,
     OFPPC_NO_FWD = 0x20,
     OFPPC_NO_PACKET_IN = 0x40,
+    OFPPC_BSN_MIRROR_DEST = 0x80000000,
 };
 
 enum ofp_port_state(wire_type=uint32_t, bitmask=True) {