Merge remote-tracking branch 'upstream/master'

Conflicts:
	c_gen/c_test_gen.py
	java_gen/java_type.py
	java_gen/pre-written/pom.xml
	java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java
	java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchFields.java
	loxi_ir/ir_offset.py
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index 8726632..c8f70e6 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -184,14 +184,25 @@
     @memoize
     def enums(self):
         name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
+        name_stable_map = {}
 
         for version in self.versions:
-            logger.info("version: {}".format(version.ir_version))
+            logger.debug("version: {}".format(version.ir_version))
             of_protocol = loxi_globals.ir[version.ir_version]
             for enum in of_protocol.enums:
                 name_version_enum_map[enum.name][version] = enum
+                stable = (enum.params.get('stable') == 'True')
 
-        enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
+                logger.debug("Enum: %s stable: %s", enum.name, stable)
+
+                if not enum.name in name_stable_map:
+                    name_stable_map[enum.name] = stable
+                else:
+                    if name_stable_map[enum.name] != stable:
+                        raise Exception("Inconsistent enum stability (should be caught " +\
+                            " by IR)")
+
+        enums = [ JavaEnum(name, name_stable_map[name], version_enum_map) for name, version_enum_map,
                         in name_version_enum_map.items() ]
 
         # inelegant - need java name here
@@ -402,6 +413,9 @@
                 reply_name = m.group(1) + "Reply"
                 if model.interface_by_name(reply_name):
                     return ["OFRequest<%s>" % reply_name ]
+            elif self.name == "OFBundleCtrlMsg":
+                reply_name = "OFBundleCtrlMsg"
+                return ["OFRequest<%s>" % reply_name ]
         return []
 
 
@@ -573,6 +587,8 @@
             if not find(lambda x: x.name == "mask", self.ir_model_members):
                 virtual_members.append(
                         JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
+        elif self.name =="OFErrorMsg":
+            virtual_members += [ JavaVirtualMember(self, "data", java_type.error_cause_data) ]
 
         if not find(lambda m: m.name == "version", self.ir_model_members):
             virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
@@ -815,7 +831,7 @@
             entry = enum.entry_by_version_value(self.msg.version, self.value)
             return "%s.%s" % ( enum.name, entry.name)
         except KeyError, e:
-            logger.debug("No enum found", e)
+            logger.debug("No enum found for type %s version %s value %s", java_type, self.msg.version, self.value)
             return self.value
 
     @property
@@ -1041,8 +1057,9 @@
 #######################################################################
 
 class JavaEnum(object):
-    def __init__(self, c_name, version_enum_map):
+    def __init__(self, c_name, stable, version_enum_map):
         self.c_name = c_name
+        self.stable = stable
 
         self.name   = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
 
@@ -1062,7 +1079,23 @@
         self.entries = [ e for e in self.entries if e.name not in model.enum_entry_blacklist[self.name] ]
         self.package = "org.projectfloodlight.openflow.protocol"
 
-        self.metadata = model.enum_metadata_map[self.name]
+        static_metadata = model.enum_metadata_map[self.name]
+        if self.stable:
+            # need this to look up wire_type, which does not matter
+            any_version = version_enum_map.keys()[0]
+            # if this is a 'stable' enum, i.e., its value won't change, add
+            # a "Metadata" (virtual) field "StableValue" to it that returns
+            # its wirevalue.
+            stable_value = JavaModel.OFEnumPropertyMetadata("StableValue",
+                    self.wire_type(any_version),
+                    value = lambda entry: entry.stable_value)
+
+            self.metadata = JavaModel.OFEnumMetadata(
+                              properties=static_metadata.properties + (stable_value, ),
+                              to_string=static_metadata.to_string
+                            )
+        else:
+            self.metadata = static_metadata
 
     def wire_type(self, version):
         ir_enum = self.version_enums[version]
@@ -1113,7 +1146,7 @@
 
     @property
     def constructor_params(self):
-        return [ m.value(self) for m in self.enum.metadata.properties ]
+        return [ (m.type, m.value(self)) for m in self.enum.metadata.properties ]
 
     def has_value(self, version):
         return version in self.values
@@ -1129,6 +1162,13 @@
         return [ self.values[version] if version in self.values else not_present for version in versions ]
 
     @property
+    def stable_value(self):
+        if self.enum.stable:
+            return self.values.values()[0]
+        else:
+            raise Exception("Enum {} not stable".format(self.enum.name))
+
+    @property
     @memoize
     def masked_enum_group(self):
         group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 5ff987d..900aa6d 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -53,9 +53,12 @@
         signed-craziness
     """
     signed, bits, cast_needed = java_primitives_info[t]
+    if t == 'boolean':
+        return "true" if bool(value) and value not in("False", "false") else "false"
+
     max = (1 << bits)-1
     if value > max:
-        raise Exception("Value %d to large for type %s" % (value, t))
+        raise Exception("Value %s to large for type %s" % (value, t))
 
     if signed:
         max_pos = (1 << (bits-1)) - 1
@@ -361,10 +364,20 @@
         .op(read="IPv4Address.read4Bytes(bb)", \
             write="$name.write4Bytes(bb)",
             default='IPv4Address.NONE')
+ipv4_list =  JType('List<IPv4Address>') \
+        .op(read='ChannelUtils.readList(bb, $length, IPv4Address.READER)',
+            write='ChannelUtils.writeList(bb, $name)',
+            default='ImmutableList.<IPv4Address>of()',
+            funnel="FunnelUtils.putList($name, sink)")
 ipv6 = JType("IPv6Address") \
         .op(read="IPv6Address.read16Bytes(bb)", \
             write="$name.write16Bytes(bb)",
             default='IPv6Address.NONE')
+ipv6_list =  JType('List<IPv46ddress>') \
+        .op(read='ChannelUtils.readList(bb, $length, IPv6Address.READER)',
+            write='ChannelUtils.writeList(bb, $name)',
+            default='ImmutableList.<IPv6Address>of()',
+            funnel="FunnelUtils.putList($name, sink)")
 packetin_reason = gen_enum_jtype("OFPacketInReason")
 transport_port = JType("TransportPort")\
         .op(read="TransportPort.read2Bytes(bb)",
@@ -437,10 +450,14 @@
 table_stats_wildcards = JType("int") \
         .op(read='bb.readInt()',
             write='bb.writeInt($name)')
-port_bitmap = JType('OFBitMask128') \
+port_bitmap_128 = JType('OFBitMask128') \
             .op(read='OFBitMask128.read16Bytes(bb)',
                 write='$name.write16Bytes(bb)',
                 default='OFBitMask128.NONE')
+port_bitmap_512 = JType('OFBitMask512') \
+            .op(read='OFBitMask512.read64Bytes(bb)',
+                write='$name.write64Bytes(bb)',
+                default='OFBitMask512.NONE')
 table_id = JType("TableId") \
         .op(read='TableId.readByte(bb)',
             write='$name.writeByte(bb)',
@@ -458,6 +475,10 @@
 
 port_speed = JType("PortSpeed")
 error_type = JType("OFErrorType")
+of_message = JType("OFMessage")\
+            .op(read="OFMessageVer$version.READER.readFrom(bb)",
+                write="$name.writeTo(bb)")
+
 of_type = JType("OFType", 'byte') \
             .op(read='bb.readByte()', write='bb.writeByte($name)')
 action_type= gen_enum_jtype("OFActionType")\
@@ -505,6 +526,10 @@
         .op(read='GenTableId.read2Bytes(bb)',
             write='$name.write2Bytes(bb)',
            )
+bundle_id = JType("BundleId") \
+        .op(read='BundleId.read4Bytes(bb)',
+            write='$name.write4Bytes(bb)',
+           )
 udf = JType("UDF") \
          .op(version=ANY, read="UDF.read4Bytes(bb)", write="$name.write4Bytes(bb)", default="UDF.ZERO")
 error_cause_data = JType("OFErrorCauseData") \
@@ -519,6 +544,10 @@
 
 generic_t = JType("T")
 
+table_desc = JType('OFTableDesc') \
+        .op(read='OFTableDescVer$version.READER.readFrom(bb)', \
+            write='$name.writeTo(bb)')
+
 
 default_mtype_to_jtype_convert_map = {
         'uint8_t' : u8,
@@ -535,6 +564,8 @@
         'list(of_uint32_t)' : u32_list,
         'list(of_uint8_t)' : u8_list,
         'list(of_oxm_t)' : oxm_list,
+        'list(of_ipv4_t)' : ipv4_list,
+        'list(of_ipv6_t)' : ipv6_list,
         'of_octets_t' : octets,
         'of_match_t': of_match,
         'of_fm_cmd_t': flow_mod_cmd,
@@ -550,11 +581,13 @@
         'of_wc_bmap_t': flow_wildcards,
         'of_oxm_t': oxm,
         'of_meter_features_t': meter_features,
-        'of_bitmap_128_t': port_bitmap,
+        'of_bitmap_128_t': port_bitmap_128,
+        'of_bitmap_512_t': port_bitmap_512,
         'of_checksum_128_t': u128,
         'of_bsn_vport_t': bsn_vport,
         'of_app_code_t': app_code,  
         'of_sig_id_t': sig_id,
+        'of_table_desc_t': table_desc,
         }
 
 ## Map that defines exceptions from the standard loxi->java mapping scheme
@@ -608,9 +641,18 @@
         'of_oxm_mpls_label_masked' : { 'value' : u32obj, 'value_mask' : u32obj },
         'of_oxm_mpls_tc' : { 'value' : u8obj },
         'of_oxm_mpls_tc_masked' : { 'value' : u8obj, 'value_mask' : u8obj },
+        'of_oxm_mpls_bos' : { 'value' : boolean_value },
+        'of_oxm_mpls_bos_masked' : { 'value' : boolean_value, 'value_mask' : boolean_value },
+        'of_oxm_ipv6_exthdr' : { 'value' : u16obj },
+        'of_oxm_ipv6_exthdr_masked' : { 'value' : u16obj, 'value_mask' : u16obj },
+        'of_oxm_pbb_uca' : { 'value' : boolean_value },
+        'of_oxm_pbb_uca_masked' : { 'value' : boolean_value, 'value_mask' : boolean_value },
 
-        'of_oxm_bsn_in_ports_128' : { 'value': port_bitmap },
-        'of_oxm_bsn_in_ports_128_masked' : { 'value': port_bitmap, 'value_mask': port_bitmap },
+        'of_oxm_bsn_in_ports_128' : { 'value': port_bitmap_128 },
+        'of_oxm_bsn_in_ports_128_masked' : { 'value': port_bitmap_128, 'value_mask': port_bitmap_128 },
+
+        'of_oxm_bsn_in_ports_512' : { 'value': port_bitmap_512 },
+        'of_oxm_bsn_in_ports_512_masked' : { 'value': port_bitmap_512, 'value_mask': port_bitmap_512 },
 
         'of_oxm_bsn_lag_id' : { 'value' : lag_id },
         'of_oxm_bsn_lag_id_masked' : { 'value' : lag_id, 'value_mask' : lag_id },
@@ -663,6 +705,9 @@
         'of_oxm_bsn_vlan_xlate_port_group_id' : { 'value' : class_id },
         'of_oxm_bsn_vlan_xlate_port_group_id_masked' : { 'value' : class_id, 'value_mask' : class_id },
 
+        'of_oxm_bsn_l2_cache_hit' : { 'value' : boolean_value },
+        'of_oxm_bsn_l2_cache_hit_masked' : { 'value' : boolean_value, 'value_mask' : boolean_value },
+
         'of_table_stats_entry': { 'wildcards': table_stats_wildcards },
         'of_match_v1': { 'vlan_vid' : vlan_vid_match, 'vlan_pcp': vlan_pcp,
                 'eth_type': eth_type, 'ip_dscp': ip_dscp, 'ip_proto': ip_proto,
@@ -704,8 +749,10 @@
         'of_features_reply' : { 'auxiliary_id' : of_aux_id},
         'of_oxm_och_sigtype' : { 'value' : u8obj },
         'of_oxm_och_sigtype_basic' : { 'value' : u8obj },
-	'of_oxm_och_sigid' : {'value' : sig_id},
-	'of_oxm_och_sigid_basic' : {'value' : sig_id},  
+        'of_oxm_och_sigid' : {'value' : sig_id},
+        'of_oxm_och_sigid_basic' : {'value' : sig_id},  
+
+        'of_bundle_add_msg' : { 'data' : of_message },
 }
 
 
@@ -770,6 +817,8 @@
         return action_type_set
     elif field_name == "table_id" and re.match(r'of_bsn_gentable.*', obj_name):
         return gen_table_id
+    elif field_name == "bundle_id" and re.match(r'of_bundle_.*', obj_name):
+        return bundle_id
     elif c_type in default_mtype_to_jtype_convert_map:
         return default_mtype_to_jtype_convert_map[c_type]
     elif re.match(r'list\(of_([a-zA-Z_]+)_t\)', c_type):
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerator.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerator.java
new file mode 100644
index 0000000..2cb3583
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerator.java
@@ -0,0 +1,7 @@
+package org.projectfloodlight.openflow.protocol;
+
+import org.projectfloodlight.openflow.types.BundleId;
+
+public interface BundleIdGenerator {
+    BundleId nextBundleId();
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerators.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerators.java
new file mode 100644
index 0000000..997e0cd
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerators.java
@@ -0,0 +1,28 @@
+package org.projectfloodlight.openflow.protocol;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.projectfloodlight.openflow.types.BundleId;
+
+public class BundleIdGenerators {
+    private static final BundleIdGenerator GLOBAL_BUNDLE_ID_GENERATOR = create();
+
+    public static BundleIdGenerator create() {
+        return new StandardBundleIdGenerator();
+    }
+
+    public static BundleIdGenerator global() {
+        return GLOBAL_BUNDLE_ID_GENERATOR;
+    }
+}
+
+class StandardBundleIdGenerator implements BundleIdGenerator {
+
+    private final AtomicInteger idGen = new AtomicInteger();
+
+    @Override
+    public BundleId nextBundleId() {
+        return BundleId.of(idGen.incrementAndGet());
+    }
+
+}
\ No newline at end of file
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFVersion.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFVersion.java
index 6f54e5f..0c54fdc 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFVersion.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/OFVersion.java
@@ -1,7 +1,7 @@
 package org.projectfloodlight.openflow.protocol;
 
 public enum OFVersion {
-    OF_10(1), OF_11(2), OF_12(3), OF_13(4);
+    OF_10(1), OF_11(2), OF_12(3), OF_13(4), OF_14(5);
 
     public final int wireVersion;
 
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java
index 9fb3fd4..e2f172b 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchField.java
@@ -14,6 +14,7 @@
 import org.projectfloodlight.openflow.types.LagId;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBitMask128;
+import org.projectfloodlight.openflow.types.OFBitMask512;
 import org.projectfloodlight.openflow.types.OFBooleanValue;
 import org.projectfloodlight.openflow.types.OFMetadata;
 import org.projectfloodlight.openflow.types.OFPort;
@@ -179,12 +180,33 @@
             new MatchField<U8>("mpls_tc", MatchFields.MPLS_TC,
                     new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.MPLS_UNICAST, EthType.MPLS_MULTICAST));
 
-    public final static MatchField<U64> TUNNEL_ID = 
+    public final static MatchField<OFBooleanValue> MPLS_BOS =
+            new MatchField<OFBooleanValue>("mpls_bos", MatchFields.MPLS_BOS,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.MPLS_UNICAST, EthType.MPLS_MULTICAST));
+
+    public final static MatchField<U64> TUNNEL_ID =
             new MatchField<U64>("tunnel_id", MatchFields.TUNNEL_ID);
 
+    public final static MatchField<U16> IPV6_EXTHDR =
+            new MatchField<U16>("ipv6_exthdr", MatchFields.IPV6_EXTHDR);
+
+    public final static MatchField<OFBooleanValue> PBB_UCA =
+            new MatchField<OFBooleanValue>("pbb_uca", MatchFields.PBB_UCA);
+
+    public final static MatchField<IPv4Address> TUNNEL_IPV4_SRC =
+            new MatchField<IPv4Address>("tunnel_ipv4_src", MatchFields.TUNNEL_IPV4_SRC,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.IPv4));
+
+    public final static MatchField<IPv4Address> TUNNEL_IPV4_DST =
+            new MatchField<IPv4Address>("tunnel_ipv4_dst", MatchFields.TUNNEL_IPV4_DST,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.IPv4));
+
     public final static MatchField<OFBitMask128> BSN_IN_PORTS_128 =
             new MatchField<OFBitMask128>("bsn_in_ports_128", MatchFields.BSN_IN_PORTS_128);
 
+    public final static MatchField<OFBitMask512> BSN_IN_PORTS_512 =
+            new MatchField<OFBitMask512>("bsn_in_ports_512", MatchFields.BSN_IN_PORTS_512);
+
     public final static MatchField<LagId> BSN_LAG_ID =
             new MatchField<LagId>("bsn_lag_id", MatchFields.BSN_LAG_ID);
 
@@ -236,7 +258,6 @@
     public final static MatchField<ClassId> BSN_VLAN_XLATE_PORT_GROUP_ID =
             new MatchField<ClassId>("bsn_vlan_xlate_port_group_id", MatchFields.BSN_VLAN_XLATE_PORT_GROUP_ID);
 
-
     public final static MatchField<U8> OCH_SIGTYPE =
             new MatchField<U8>("och_sigtype",
                                     MatchFields.OCH_SIGTYPE);
@@ -245,7 +266,6 @@
             new MatchField<U8>("och_sigtype_basic",
                                     MatchFields.OCH_SIGTYPE_BASIC);
 
-
     public final static MatchField<CircuitSignalID> OCH_SIGID =
             new MatchField<CircuitSignalID>("och_sigid",
                                     MatchFields.OCH_SIGID);
@@ -254,6 +274,9 @@
             new MatchField<CircuitSignalID>("och_sigid_basic",
                                     MatchFields.OCH_SIGID);
     
+    public final static MatchField<OFBooleanValue> BSN_L2_CACHE_HIT =
+            new MatchField<OFBooleanValue>("bsn_l2_cache_hit", MatchFields.BSN_L2_CACHE_HIT);
+
     public String getName() {
         return name;
     }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchFields.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchFields.java
index 3bf717b..12e2b20 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchFields.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/match/MatchFields.java
@@ -38,8 +38,14 @@
     IPV6_ND_TLL,
     MPLS_LABEL,
     MPLS_TC,
+    MPLS_BOS,
     TUNNEL_ID,
+    IPV6_EXTHDR,
+    PBB_UCA,
+    TUNNEL_IPV4_SRC,
+    TUNNEL_IPV4_DST,
     BSN_IN_PORTS_128,
+    BSN_IN_PORTS_512,
     BSN_LAG_ID,
     BSN_VRF,
     BSN_GLOBAL_VRF_ALLOWED,
@@ -57,8 +63,9 @@
     BSN_UDF7,
     BSN_TCP_FLAGS,
     BSN_VLAN_XLATE_PORT_GROUP_ID,
+    BSN_L2_CACHE_HIT,
     OCH_SIGTYPE,
     OCH_SIGTYPE_BASIC,
     OCH_SIGID,
-    OCH_SIGID_BASIC,;
+    OCH_SIGID_BASIC,
 }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/ver14/ChannelUtilsVer14.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/ver14/ChannelUtilsVer14.java
new file mode 100644
index 0000000..c893cab
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/ver14/ChannelUtilsVer14.java
@@ -0,0 +1,26 @@
+package org.projectfloodlight.openflow.protocol.ver14;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+import org.projectfloodlight.openflow.protocol.OFMatchBmap;
+import org.projectfloodlight.openflow.protocol.match.Match;
+
+/**
+ * Collection of helper functions for reading and writing into ChannelBuffers
+ *
+ * @author capveg
+ */
+
+public class ChannelUtilsVer14 {
+    public static Match readOFMatch(final ChannelBuffer bb) throws OFParseError {
+        return OFMatchV3Ver14.READER.readFrom(bb);
+    }
+
+    public static OFMatchBmap readOFMatchBmap(ChannelBuffer bb) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    public static void writeOFMatchBmap(ChannelBuffer bb, OFMatchBmap match) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/BundleId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/BundleId.java
new file mode 100644
index 0000000..cccf67e
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/BundleId.java
@@ -0,0 +1,92 @@
+package org.projectfloodlight.openflow.types;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+import com.google.common.hash.PrimitiveSink;
+import com.google.common.primitives.UnsignedInts;
+
+@Immutable
+public class BundleId implements OFValueType<BundleId> {
+    static final int LENGTH = 4;
+
+    private final static int NONE_VAL = 0;
+    public final static BundleId NONE = new BundleId(NONE_VAL);
+
+    private final static int NO_MASK_VAL = 0xFFFFFFFF;
+    public final static BundleId NO_MASK = new BundleId(NO_MASK_VAL);
+    public final static BundleId FULL_MASK = NONE;
+
+    private final int rawValue;
+
+    private BundleId(final int rawValue) {
+        this.rawValue = rawValue;
+    }
+
+    public static BundleId of(final int raw) {
+        if(raw == NONE_VAL)
+            return NONE;
+        else if(raw == NO_MASK_VAL)
+            return NO_MASK;
+        return new BundleId(raw);
+    }
+
+    public int getInt() {
+        return rawValue;
+    }
+
+    @Override
+    public int getLength() {
+        return LENGTH;
+    }
+
+    @Override
+    public String toString() {
+        return UnsignedInts.toString(rawValue);
+    }
+
+    @Override
+    public BundleId applyMask(BundleId mask) {
+        return BundleId.of(rawValue & mask.rawValue);    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + rawValue;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BundleId other = (BundleId) obj;
+        if (rawValue != other.rawValue)
+            return false;
+        return true;
+    }
+
+    public void write4Bytes(ChannelBuffer c) {
+        c.writeInt(rawValue);
+    }
+
+    public static BundleId read4Bytes(ChannelBuffer c) {
+        return BundleId.of(c.readInt());
+    }
+
+    @Override
+    public int compareTo(BundleId o) {
+        return UnsignedInts.compare(rawValue, rawValue);
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) {
+        sink.putInt(rawValue);
+    }
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
index 7d7c38e..98c1253 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
@@ -43,7 +43,7 @@
 
     @Override
     public String toString() {
-        return Integer.toString(rawValue);
+        return UnsignedInts.toString(rawValue);
     }
 
     @Override
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4Address.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4Address.java
index 3a1b15e..eb37a20 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4Address.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv4Address.java
@@ -12,14 +12,16 @@
 import com.google.common.hash.PrimitiveSink;
 import com.google.common.primitives.UnsignedInts;
 
-
+import org.projectfloodlight.openflow.protocol.Writeable;
+import org.projectfloodlight.openflow.protocol.OFMessageReader;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
 
 /**
  * Wrapper around an IPv4Address address
  *
  * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
  */
-public class IPv4Address extends IPAddress<IPv4Address> {
+public class IPv4Address extends IPAddress<IPv4Address> implements Writeable {
     static final int LENGTH = 4;
     private final int rawValue;
 
@@ -38,6 +40,15 @@
         this.rawValue = rawValue;
     }
 
+    public final static Reader READER = new Reader();
+
+    private static class Reader implements OFMessageReader<IPv4Address> {
+        @Override
+        public IPv4Address readFrom(ChannelBuffer bb) throws OFParseError {
+            return new IPv4Address(bb.readInt());
+        }
+    }
+
     @Override
     public IPVersion getIpVersion() {
         return IPVersion.IPv4;
@@ -345,4 +356,8 @@
         sink.putInt(rawValue);
     }
 
+    @Override
+    public void writeTo(ChannelBuffer bb) {
+        bb.writeInt(rawValue);
+    }
 }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6Address.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6Address.java
index 471d0fb..9d6fa4d 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6Address.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/IPv6Address.java
@@ -8,19 +8,22 @@
 import javax.annotation.Nonnull;
 
 import org.jboss.netty.buffer.ChannelBuffer;
-import org.projectfloodlight.openflow.exceptions.OFParseError;
 
 import com.google.common.base.Preconditions;
 import com.google.common.hash.PrimitiveSink;
 import com.google.common.primitives.Longs;
 
+import org.projectfloodlight.openflow.protocol.Writeable;
+import org.projectfloodlight.openflow.protocol.OFMessageReader;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+
 /**
  * IPv6 address object. Instance controlled, immutable. Internal representation:
  * two 64 bit longs (not that you'd have to know).
  *
  * @author Andreas Wundsam <andreas.wundsam@teleteach.de>
  */
-public class IPv6Address extends IPAddress<IPv6Address> {
+public class IPv6Address extends IPAddress<IPv6Address> implements Writeable {
     static final int LENGTH = 16;
     private final long raw1;
     private final long raw2;
@@ -43,6 +46,15 @@
         this.raw2 = raw2;
     }
 
+    public final static Reader READER = new Reader();
+
+    private static class Reader implements OFMessageReader<IPv6Address> {
+        @Override
+        public IPv6Address readFrom(ChannelBuffer bb) throws OFParseError {
+            return new IPv6Address(bb.readLong(), bb.readLong());
+        }
+    }
+
     @Override
     public IPVersion getIpVersion() {
         return IPVersion.IPv6;
@@ -213,8 +225,17 @@
     public static IPv6Address of(@Nonnull final String string) throws IllegalArgumentException {
         Preconditions.checkNotNull(string, "string must not be null");
 
+        // remove the zone id
+        int zoneDelimIndex = string.indexOf("%");
+        String substring;
+        if (zoneDelimIndex != -1) {
+            substring = string.substring(0, zoneDelimIndex);
+        } else {
+            substring = string;
+        }
+
         IPv6Builder builder = new IPv6Builder();
-        String[] parts = colonPattern.split(string, -1);
+        String[] parts = colonPattern.split(substring, -1);
 
         int leftWord = 0;
         int leftIndex = 0;
@@ -538,4 +559,10 @@
         sink.putLong(raw1);
         sink.putLong(raw2);
     }
+
+    @Override
+    public void writeTo(ChannelBuffer bb) {
+        bb.writeLong(raw1);
+        bb.writeLong(raw2);
+    }
 }
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBitMask512.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBitMask512.java
new file mode 100644
index 0000000..3fe9b88
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFBitMask512.java
@@ -0,0 +1,205 @@
+package org.projectfloodlight.openflow.types;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+import com.google.common.hash.PrimitiveSink;
+
+public class OFBitMask512 implements OFValueType<OFBitMask512> {
+
+    static final int LENGTH = 64;
+
+    private final long raw1;
+    private final long raw2;
+    private final long raw3;
+    private final long raw4;
+    private final long raw5;
+    private final long raw6;
+    private final long raw7;
+    private final long raw8;
+
+    public static final OFBitMask512 ALL = new OFBitMask512(-1, -1, -1, -1,
+                                                            -1, -1, -1, -1);
+    public static final OFBitMask512 NONE = new OFBitMask512(0, 0, 0, 0,
+                                                             0, 0, 0, 0);
+
+    public static final OFBitMask512 NO_MASK = ALL;
+    public static final OFBitMask512 FULL_MASK = NONE;
+
+    private OFBitMask512(long raw1, long raw2, long raw3, long raw4,
+                         long raw5, long raw6, long raw7, long raw8) {
+        this.raw1 = raw1;
+        this.raw2 = raw2;
+        this.raw3 = raw3;
+        this.raw4 = raw4;
+        this.raw5 = raw5;
+        this.raw6 = raw6;
+        this.raw7 = raw7;
+        this.raw8 = raw8;
+    }
+
+    public static OFBitMask512 of(long raw1, long raw2, long raw3, long raw4,
+                                  long raw5, long raw6, long raw7, long raw8) {
+        if (raw1 == -1 && raw2 == -1 && raw3 == -1 && raw4 == -1
+                && raw5 == -1 && raw6 == -1 && raw7 == -1 && raw8 == -1)
+            return ALL;
+        if (raw1 == 0 && raw2 == 0 && raw3 == 0 && raw4 == 0
+                && raw5 == 0 && raw6 == 0 && raw7 == 0 && raw8 == 0)
+            return NONE;
+        return new OFBitMask512(raw1, raw2, raw3, raw4, raw5, raw6, raw7, raw8);
+    }
+
+    @Override
+    public int getLength() {
+        return LENGTH;
+    }
+
+    @Override
+    public OFBitMask512 applyMask(OFBitMask512 mask) {
+        return of(this.raw1 & mask.raw1, this.raw2 & mask.raw2,
+                  this.raw3 & mask.raw3, this.raw4 & mask.raw4,
+                  this.raw5 & mask.raw5, this.raw6 & mask.raw6,
+                  this.raw7 & mask.raw7, this.raw8 & mask.raw8);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
+        result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
+        result = prime * result + (int) (raw3 ^ (raw3 >>> 32));
+        result = prime * result + (int) (raw4 ^ (raw4 >>> 32));
+        result = prime * result + (int) (raw5 ^ (raw5 >>> 32));
+        result = prime * result + (int) (raw6 ^ (raw6 >>> 32));
+        result = prime * result + (int) (raw7 ^ (raw7 >>> 32));
+        result = prime * result + (int) (raw8 ^ (raw8 >>> 32));
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        OFBitMask512 other = (OFBitMask512) obj;
+        if (raw1 != other.raw1) return false;
+        if (raw2 != other.raw2) return false;
+        if (raw3 != other.raw3) return false;
+        if (raw4 != other.raw4) return false;
+        if (raw5 != other.raw5) return false;
+        if (raw6 != other.raw6) return false;
+        if (raw7 != other.raw7) return false;
+        if (raw8 != other.raw8) return false;
+        return true;
+    }
+
+    protected static boolean isBitOn(long raw1, long raw2, long raw3, long raw4,
+                                     long raw5, long raw6, long raw7, long raw8, int bit) {
+        if (bit < 0 || bit >= 512)
+            throw new IndexOutOfBoundsException();
+        long word;
+        if (bit < 64) {
+            word = raw8;
+        } else if (bit < 128) {
+            word = raw7;
+            bit -= 64;
+        } else if (bit < 128) {
+            word = raw6;
+            bit -= 128;
+        } else if (bit < 128) {
+            word = raw5;
+            bit -= 192;
+        } else if (bit < 128) {
+            word = raw4;
+            bit -= 256;
+        } else if (bit < 128) {
+            word = raw3;
+            bit -= 320;
+        } else if (bit < 128) {
+            word = raw2;
+            bit -= 384;
+        } else {
+            word = raw1;
+            bit -= 448;
+        }
+        return (word & ((long)1 << bit)) != 0;
+    }
+
+    public void write64Bytes(ChannelBuffer cb) {
+        cb.writeLong(raw1);
+        cb.writeLong(raw2);
+        cb.writeLong(raw3);
+        cb.writeLong(raw4);
+        cb.writeLong(raw5);
+        cb.writeLong(raw6);
+        cb.writeLong(raw7);
+        cb.writeLong(raw8);
+    }
+
+    public static OFBitMask512 read64Bytes(ChannelBuffer cb) {
+        long raw1 = cb.readLong();
+        long raw2 = cb.readLong();
+        long raw3 = cb.readLong();
+        long raw4 = cb.readLong();
+        long raw5 = cb.readLong();
+        long raw6 = cb.readLong();
+        long raw7 = cb.readLong();
+        long raw8 = cb.readLong();
+        return of(raw1, raw2, raw3, raw4, raw5, raw6, raw7, raw8);
+    }
+
+    public boolean isOn(int bit) {
+        return isBitOn(raw1, raw2, raw3, raw4, raw5, raw6, raw7, raw8, bit);
+    }
+
+    @Override
+    public String toString() {
+        return (String.format("%64s", Long.toBinaryString(raw8))
+                + String.format("%64s", Long.toBinaryString(raw7))
+                + String.format("%64s", Long.toBinaryString(raw6))
+                + String.format("%64s", Long.toBinaryString(raw5))
+                + String.format("%64s", Long.toBinaryString(raw4))
+                + String.format("%64s", Long.toBinaryString(raw3))
+                + String.format("%64s", Long.toBinaryString(raw2))
+                + String.format("%64s", Long.toBinaryString(raw1))).replaceAll(" ", "0");
+    }
+
+    @Override
+    public int compareTo(OFBitMask512 o) {
+        long c = this.raw1 - o.raw1;
+        if (c != 0)
+            return Long.signum(c);
+        c = this.raw2 - o.raw2;
+        if (c != 0)
+            return Long.signum(c);
+        c = this.raw3 - o.raw3;
+        if (c != 0)
+            return Long.signum(c);
+        c = this.raw4 - o.raw4;
+        if (c != 0)
+            return Long.signum(c);
+        c = this.raw5 - o.raw5;
+        if (c != 0)
+            return Long.signum(c);
+        c = this.raw6 - o.raw6;
+        if (c != 0)
+            return Long.signum(c);
+        c = this.raw7 - o.raw7;
+        if (c != 0)
+            return Long.signum(c);
+        return Long.signum(this.raw8 - o.raw8);
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) {
+        sink.putLong(raw1);
+        sink.putLong(raw2);
+        sink.putLong(raw3);
+        sink.putLong(raw4);
+        sink.putLong(raw5);
+        sink.putLong(raw6);
+        sink.putLong(raw7);
+        sink.putLong(raw8);
+    }
+
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/MultiplePktInReasonUtil.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/MultiplePktInReasonUtil.java
index a919f62..3ee588f 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/MultiplePktInReasonUtil.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/MultiplePktInReasonUtil.java
@@ -22,10 +22,10 @@
      * reasons in Match.MetaData field.
      * */
     public static Set<OFBsnPktinFlag> getOFBsnPktinFlags(OFPacketIn pktIn) {
-        if(pktIn.getVersion() != OFVersion.OF_13) {
+        if(pktIn.getVersion().compareTo(OFVersion.OF_13) < 0) {
             throw new IllegalArgumentException("multiple pkt in reasons are "
                                                + "only supported by BVS using "
-                                               + "openflow 1.3");
+                                               + "openflow version >= 1.3");
         }
 
         Match match = pktIn.getMatch();
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
index a397c2a..fd26856 100644
--- a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/types/IPv6AddressTest.java
@@ -399,4 +399,10 @@
             assertNotNull(e.getMessage());
         }
     }
+
+    @Test
+    public void testZoneId() throws OFParseError {
+        assertEquals("::", IPv6Address.of("::%eth0").toString(true, false));
+        assertEquals("1:0:0:4::8", IPv6Address.of("1:0:0:4:0:0:0:8%2").toString(true, false));
+    }
 }
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFPortDescTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFPortDescTest.java
new file mode 100644
index 0000000..965e314
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/protocol/OFPortDescTest.java
@@ -0,0 +1,53 @@
+package org.projectfloodlight.protocol;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFPortConfig;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortState;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+import static org.hamcrest.Matchers.is;
+
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests auxiliary OFPortDesc methods for all versions of OpenFlow
+ *
+ * @author Jason Parraga <jason.parraga@bigswitch.com>
+ */
+public class OFPortDescTest {
+
+    @Test
+    public void testIsEnabled() {
+       testIsEnabledForFactory(OFFactories.getFactory(OFVersion.OF_10));
+       testIsEnabledForFactory(OFFactories.getFactory(OFVersion.OF_11));
+       testIsEnabledForFactory(OFFactories.getFactory(OFVersion.OF_12));
+       testIsEnabledForFactory(OFFactories.getFactory(OFVersion.OF_13));
+       testIsEnabledForFactory(OFFactories.getFactory(OFVersion.OF_14));
+    }
+
+    public void testIsEnabledForFactory(OFFactory factory) {
+        // Default
+        OFPortDesc desc = factory.buildPortDesc()
+                .build();
+        assertThat(desc.isEnabled(), is(true));
+
+        // Partially disabled
+        desc = factory.buildPortDesc()
+                .setConfig(new HashSet<OFPortConfig>(Arrays.asList(OFPortConfig.PORT_DOWN)))
+                .build();
+        assertThat(desc.isEnabled(), is(false));
+
+        // Fully disabled
+        desc = factory.buildPortDesc()
+                .setConfig(new HashSet<OFPortConfig>(Arrays.asList(OFPortConfig.PORT_DOWN)))
+                .setState(new HashSet<OFPortState>(Arrays.asList(OFPortState.LINK_DOWN)))
+                .build();
+        assertThat(desc.isEnabled(), is(false));
+    }
+}
diff --git a/java_gen/templates/const.java b/java_gen/templates/const.java
index a7786f4..1828bac 100644
--- a/java_gen/templates/const.java
+++ b/java_gen/templates/const.java
@@ -37,7 +37,7 @@
 public enum ${class_name} {
 //:: for i, entry in enumerate(enum.entries):
 //::    if enum.metadata.properties:
-//::        params = "({})".format(", ".join(entry.constructor_params))
+//::        params = "({})".format(", ".join(type.format_value(value) for (type, value) in entry.constructor_params))
 //::    else:
 //::        params = ""
 //::    #endif
diff --git a/java_gen/templates/custom/OFMatchV3.Builder.java b/java_gen/templates/custom/OFMatchV3.Builder.java
new file mode 100644
index 0000000..54b35ba
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3.Builder.java
@@ -0,0 +1,106 @@
+
+    private OFOxmList.Builder oxmListBuilder;
+
+    private void initBuilder() {
+        if (oxmListBuilder != null)
+            return;
+        oxmListBuilder = new OFOxmList.Builder();
+    }
+
+    private void updateOxmList() {
+        this.oxmList = this.oxmListBuilder.build();
+        this.oxmListSet = true;
+    }
+
+    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
+//:: if has_parent:
+        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
+//:: else:
+        return this.oxmListSet ? this.oxmList.get(field) : null;
+//:: #endif
+    }
+
+    @Override
+    public <F extends OFValueType<F>> F get(MatchField<F> field)
+            throws UnsupportedOperationException {
+        OFOxm<F> value = getOxm(field);
+        if (value == null)
+            return null;
+        return value.getValue();
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
+            throws UnsupportedOperationException {
+        OFOxm<F> value = getOxm(field);
+        if (value == null || !value.isMasked())
+            return null;
+        // TODO: If changing OXMs to extend Masked, then use it here
+        return Masked.of(value.getValue(), value.getMask());
+    }
+
+    @Override
+    public boolean supports(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean supportsMasked(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean isExact(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value != null && !value.isMasked());
+    }
+
+    @Override
+    public boolean isFullyWildcarded(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value == null);
+    }
+
+    @Override
+    public boolean isPartiallyMasked(MatchField<?> field) {
+        OFOxm<?> value = getOxm(field);
+        return (value != null && value.isMasked());
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setExact(
+            MatchField<F> field, F value) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromValue(value, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setMasked(
+            MatchField<F> field, F value, F mask) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromValueAndMask(value, mask, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder setMasked(
+            MatchField<F> field, Masked<F> valueWithMask) {
+        initBuilder();
+        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.${version.constant_version}).oxms().fromMasked(valueWithMask, field);
+        this.oxmListBuilder.set(oxm);
+        updateOxmList();
+        return this;
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
+        initBuilder();
+        this.oxmListBuilder.unset(field);
+        updateOxmList();
+        return this;
+    }
diff --git a/java_gen/templates/custom/OFMatchV3.java b/java_gen/templates/custom/OFMatchV3.java
new file mode 100644
index 0000000..799135c
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3.java
@@ -0,0 +1,112 @@
+//:: from generic_utils import OrderedSet
+//:: from java_gen.java_model import model
+    @Override
+    public <F extends OFValueType<F>> F get(MatchField<F> field)
+            throws UnsupportedOperationException {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<F> oxm = this.oxmList.get(field);
+
+        if (oxm == null || !field.arePrerequisitesOK(this))
+            return null;
+
+        return oxm.getValue();
+    }
+
+    @Override
+    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
+            throws UnsupportedOperationException {
+        if (!supportsMasked(field))
+            throw new UnsupportedOperationException("${msg.name} does not support masked matching on field " + field.getName());
+
+        OFOxm<F> oxm = this.oxmList.get(field);
+
+        if (oxm == null || !field.arePrerequisitesOK(this))
+            return null;
+
+        if (oxm.getMask() == null)
+            return null;
+
+        // TODO: Make OfOxm extend Masked and just return the OXM?
+        return Masked.of(oxm.getValue(), oxm.getMask());
+    }
+
+    private static boolean supportsField(MatchField<?> field) {
+        switch (field.id) {
+            //:: for id_constant in sorted(set(id_constant for _, id_constant, _ in model.oxm_map.values())):
+            case ${id_constant}:
+            //:: #endfor
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean supports(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean supportsMasked(MatchField<?> field) {
+        return supportsField(field);
+    }
+
+    @Override
+    public boolean isExact(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm != null && !oxm.isMasked();
+    }
+
+    @Override
+    public boolean isFullyWildcarded(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm == null;
+    }
+
+    @Override
+    public boolean isPartiallyMasked(MatchField<?> field) {
+        if (!supports(field))
+            throw new UnsupportedOperationException("${msg.name} does not support matching on field " + field.getName());
+
+        OFOxm<?> oxm = this.oxmList.get(field);
+
+        return oxm != null && oxm.isMasked();
+    }
+
+    private class MatchFieldIterator extends AbstractIterator<MatchField<?>> {
+        private Iterator<OFOxm<?>> oxmIterator;
+
+        MatchFieldIterator() {
+            oxmIterator = oxmList.iterator();
+        }
+
+        @Override
+        protected MatchField<?> computeNext() {
+            while(oxmIterator.hasNext()) {
+                OFOxm<?> oxm = oxmIterator.next();
+                if(oxm.getMatchField().arePrerequisitesOK(${msg.name}.this))
+                   return oxm.getMatchField();
+            }
+            endOfData();
+            return null;
+        }
+    }
+
+    @Override
+    public Iterable<MatchField<?>> getMatchFields() {
+        return new Iterable<MatchField<?>>() {
+            public Iterator<MatchField<?>> iterator() {
+                return new MatchFieldIterator();
+            }
+        };
+    }
diff --git a/java_gen/templates/custom/OFMatchV3Ver12.Builder.java b/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
index 3fae367..ae2ecaa 100644
--- a/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
+++ b/java_gen/templates/custom/OFMatchV3Ver12.Builder.java
@@ -1,106 +1 @@
-
-    private OFOxmList.Builder oxmListBuilder;
-
-    private synchronized void initBuilder() {
-        if (oxmListBuilder != null)
-            return;
-        oxmListBuilder = new OFOxmList.Builder();
-    }
-
-    private synchronized void updateOxmList() {
-        this.oxmList = this.oxmListBuilder.build();
-        this.oxmListSet = true;
-    }
-
-    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
-//:: if has_parent:
-        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
-//:: else:
-        return this.oxmListSet ? this.oxmList.get(field) : null;
-//:: #endif
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null)
-            return null;
-        return value.getValue();
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null || !value.isMasked())
-            return null;
-        // TODO: If changing OXMs to extend Masked, then use it here
-        return Masked.of(value.getValue(), value.getMask());
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public synchronized boolean isExact(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && !value.isMasked());
-    }
-
-    @Override
-    public synchronized boolean isFullyWildcarded(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value == null);
-    }
-
-    @Override
-    public synchronized boolean isPartiallyMasked(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && value.isMasked());
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setExact(
-            MatchField<F> field, F value) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValue(value, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setMasked(
-            MatchField<F> field, F value, F mask) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValueAndMask(value, mask, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder setMasked(
-            MatchField<F> field, Masked<F> valueWithMask) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromMasked(valueWithMask, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public synchronized <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
-        initBuilder();
-        this.oxmListBuilder.unset(field);
-        updateOxmList();
-        return this;
-    }
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver12.java b/java_gen/templates/custom/OFMatchV3Ver12.java
index 81092c1..225b0dc 100644
--- a/java_gen/templates/custom/OFMatchV3Ver12.java
+++ b/java_gen/templates/custom/OFMatchV3Ver12.java
@@ -1,114 +1 @@
-
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        return oxm.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supportsMasked(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support masked matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        if (oxm.getMask() == null)
-            return null;
-
-        // TODO: Make OfOxm extend Masked and just return the OXM?
-        return Masked.of(oxm.getValue(), oxm.getMask());
-    }
-
-    private static boolean supportsField(MatchField<?> field) {
-        switch (field.id) {
-            case IN_PORT:
-            case IN_PHY_PORT:
-            case METADATA:
-            case ETH_DST:
-            case ETH_SRC:
-            case ETH_TYPE:
-            case VLAN_VID:
-            case VLAN_PCP:
-            case IP_DSCP:
-            case IP_ECN:
-            case IP_PROTO:
-            case IPV4_SRC:
-            case IPV4_DST:
-            case TCP_SRC:
-            case TCP_DST:
-            case UDP_SRC:
-            case UDP_DST:
-            case SCTP_SRC:
-            case SCTP_DST:
-            case ICMPV4_TYPE:
-            case ICMPV4_CODE:
-            case ARP_OP:
-            case ARP_SPA:
-            case ARP_TPA:
-            case ARP_SHA:
-            case ARP_THA:
-            case IPV6_SRC:
-            case IPV6_DST:
-            case IPV6_FLABEL:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && !oxm.isMasked();
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm == null;
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && oxm.isMasked();
-    }
-
-    @Override
-    public Iterable<MatchField<?>> getMatchFields() {
-        throw new UnsupportedOperationException();
-    }
+//:: include("custom/OFMatchV3.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver12_toString.java b/java_gen/templates/custom/OFMatchV3Ver12_toString.java
new file mode 100644
index 0000000..3b2783b
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver12_toString.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatch_toString.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.Builder.java b/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
index 79cbdbc..ae2ecaa 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.Builder.java
@@ -1,107 +1 @@
-
-    private OFOxmList.Builder oxmListBuilder;
-
-    private void initBuilder() {
-        if (oxmListBuilder != null)
-            return;
-        oxmListBuilder = new OFOxmList.Builder();
-    }
-
-    private void updateOxmList() {
-        this.oxmList = this.oxmListBuilder.build();
-        this.oxmListSet = true;
-    }
-
-    private <F extends OFValueType<F>> OFOxm<F> getOxm(MatchField<F> field) {
-//:: if has_parent:
-        return this.oxmListSet ? this.oxmList.get(field) : parentMessage.oxmList.get(field);
-//:: else:
-        return this.oxmListSet ? this.oxmList.get(field) : null;
-//:: #endif
-    }
-
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null)
-            return null;
-        return value.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        OFOxm<F> value = getOxm(field);
-        if (value == null || !value.isMasked())
-            return null;
-        // TODO: If changing OXMs to extend Masked, then use it here
-        return Masked.of(value.getValue(), value.getMask());
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && !value.isMasked());
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value == null);
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        OFOxm<?> value = getOxm(field);
-        return (value != null && value.isMasked());
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder setExact(
-            MatchField<F> field, F value) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValue(value, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder setMasked(
-            MatchField<F> field, F value, F mask) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromValueAndMask(value, mask, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder setMasked(
-            MatchField<F> field, Masked<F> valueWithMask) {
-        initBuilder();
-        OFOxm<F> oxm = OFFactories.getFactory(OFVersion.OF_13).oxms().fromMasked(valueWithMask, field);
-        this.oxmListBuilder.set(oxm);
-        updateOxmList();
-        return this;
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Match.Builder wildcard(MatchField<F> field) {
-        initBuilder();
-        this.oxmListBuilder.unset(field);
-        updateOxmList();
-        return this;
-    }
-
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver13.java b/java_gen/templates/custom/OFMatchV3Ver13.java
index dc8e637..225b0dc 100644
--- a/java_gen/templates/custom/OFMatchV3Ver13.java
+++ b/java_gen/templates/custom/OFMatchV3Ver13.java
@@ -1,112 +1 @@
-//:: from generic_utils import OrderedSet
-//:: from java_gen.java_model import model
-    @Override
-    public <F extends OFValueType<F>> F get(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        return oxm.getValue();
-    }
-
-    @Override
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field)
-            throws UnsupportedOperationException {
-        if (!supportsMasked(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support masked matching on field " + field.getName());
-
-        OFOxm<F> oxm = this.oxmList.get(field);
-
-        if (oxm == null || !field.arePrerequisitesOK(this))
-            return null;
-
-        if (oxm.getMask() == null)
-            return null;
-
-        // TODO: Make OfOxm extend Masked and just return the OXM?
-        return Masked.of(oxm.getValue(), oxm.getMask());
-    }
-
-    private static boolean supportsField(MatchField<?> field) {
-        switch (field.id) {
-            //:: for id_constant in sorted(set(id_constant for _, id_constant, _ in model.oxm_map.values())):
-            case ${id_constant}:
-            //:: #endfor
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    @Override
-    public boolean supports(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean supportsMasked(MatchField<?> field) {
-        return supportsField(field);
-    }
-
-    @Override
-    public boolean isExact(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && !oxm.isMasked();
-    }
-
-    @Override
-    public boolean isFullyWildcarded(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm == null;
-    }
-
-    @Override
-    public boolean isPartiallyMasked(MatchField<?> field) {
-        if (!supports(field))
-            throw new UnsupportedOperationException("OFMatchV3Ver13 does not support matching on field " + field.getName());
-
-        OFOxm<?> oxm = this.oxmList.get(field);
-
-        return oxm != null && oxm.isMasked();
-    }
-
-    private class MatchFieldIterator extends AbstractIterator<MatchField<?>> {
-        private Iterator<OFOxm<?>> oxmIterator;
-
-        MatchFieldIterator() {
-            oxmIterator = oxmList.iterator();
-        }
-
-        @Override
-        protected MatchField<?> computeNext() {
-            while(oxmIterator.hasNext()) {
-                OFOxm<?> oxm = oxmIterator.next();
-                if(oxm.getMatchField().arePrerequisitesOK(OFMatchV3Ver13.this))
-                   return oxm.getMatchField();
-            }
-            endOfData();
-            return null;
-        }
-    }
-
-    @Override
-    public Iterable<MatchField<?>> getMatchFields() {
-        return new Iterable<MatchField<?>>() {
-            public Iterator<MatchField<?>> iterator() {
-                return new MatchFieldIterator();
-            }
-        };
-    }
+//:: include("custom/OFMatchV3.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver14.Builder.java b/java_gen/templates/custom/OFMatchV3Ver14.Builder.java
new file mode 100644
index 0000000..ae2ecaa
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14.Builder.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatchV3.Builder.java", msg=msg, version=version, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver14.java b/java_gen/templates/custom/OFMatchV3Ver14.java
new file mode 100644
index 0000000..225b0dc
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatchV3.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFMatchV3Ver14_toString.java b/java_gen/templates/custom/OFMatchV3Ver14_toString.java
new file mode 100644
index 0000000..3b2783b
--- /dev/null
+++ b/java_gen/templates/custom/OFMatchV3Ver14_toString.java
@@ -0,0 +1 @@
+//:: include("custom/OFMatch_toString.java", msg=msg, has_parent=False)
diff --git a/java_gen/templates/custom/OFPortDesc.java b/java_gen/templates/custom/OFPortDesc.java
new file mode 100644
index 0000000..3be1b70
--- /dev/null
+++ b/java_gen/templates/custom/OFPortDesc.java
@@ -0,0 +1,11 @@
+
+    /**
+     * Returns true if the port is up, i.e., it's neither administratively
+     * down nor link down. It currently does NOT take STP state into
+     * consideration
+     * @return whether the port is up
+     */
+    public boolean isEnabled() {
+        return (!state.contains(OFPortState.LINK_DOWN) && !config.contains(OFPortConfig.PORT_DOWN));
+    }
+    
\ No newline at end of file
diff --git a/java_gen/templates/custom/OFPortDescVer10.java b/java_gen/templates/custom/OFPortDescVer10.java
new file mode 100644
index 0000000..782a726
--- /dev/null
+++ b/java_gen/templates/custom/OFPortDescVer10.java
@@ -0,0 +1 @@
+//:: include("custom/OFPortDesc.java", msg=msg, has_parent=False)
\ No newline at end of file
diff --git a/java_gen/templates/custom/OFPortDescVer11.java b/java_gen/templates/custom/OFPortDescVer11.java
new file mode 100644
index 0000000..782a726
--- /dev/null
+++ b/java_gen/templates/custom/OFPortDescVer11.java
@@ -0,0 +1 @@
+//:: include("custom/OFPortDesc.java", msg=msg, has_parent=False)
\ No newline at end of file
diff --git a/java_gen/templates/custom/OFPortDescVer12.java b/java_gen/templates/custom/OFPortDescVer12.java
new file mode 100644
index 0000000..782a726
--- /dev/null
+++ b/java_gen/templates/custom/OFPortDescVer12.java
@@ -0,0 +1 @@
+//:: include("custom/OFPortDesc.java", msg=msg, has_parent=False)
\ No newline at end of file
diff --git a/java_gen/templates/custom/OFPortDescVer13.java b/java_gen/templates/custom/OFPortDescVer13.java
new file mode 100644
index 0000000..782a726
--- /dev/null
+++ b/java_gen/templates/custom/OFPortDescVer13.java
@@ -0,0 +1 @@
+//:: include("custom/OFPortDesc.java", msg=msg, has_parent=False)
\ No newline at end of file
diff --git a/java_gen/templates/custom/OFPortDescVer14.java b/java_gen/templates/custom/OFPortDescVer14.java
new file mode 100644
index 0000000..782a726
--- /dev/null
+++ b/java_gen/templates/custom/OFPortDescVer14.java
@@ -0,0 +1 @@
+//:: include("custom/OFPortDesc.java", msg=msg, has_parent=False)
\ No newline at end of file
diff --git a/java_gen/templates/custom/interface/OFPortDesc.java b/java_gen/templates/custom/interface/OFPortDesc.java
new file mode 100644
index 0000000..ecf39f3
--- /dev/null
+++ b/java_gen/templates/custom/interface/OFPortDesc.java
@@ -0,0 +1,2 @@
+    // Additional method
+    boolean isEnabled();
\ No newline at end of file
diff --git a/java_gen/templates/of_class.java b/java_gen/templates/of_class.java
index f1d72b2..0b032ac 100644
--- a/java_gen/templates/of_class.java
+++ b/java_gen/templates/of_class.java
@@ -74,6 +74,13 @@
     ${impl_class}(${
         ", ".join("%s %s" %(prop.java_type.public_type, prop.name) for prop in msg.data_members) }) {
 //:: for prop in msg.data_members:
+//::   if not prop.java_type.is_primitive and (not prop.default_value or prop.default_value != "null"):
+        if(${prop.name} == null) {
+            throw new NullPointerException("${msg.name}: property ${prop.name} cannot be null");
+        }
+//::   #endif
+//:: #endfor
+//:: for prop in msg.data_members:
         this.${prop.name} = ${prop.name};
 //:: #endfor
     }
@@ -88,7 +95,7 @@
     //:: include("_field_accessors.java", msg=msg, generate_setters=False, builder=False, has_parent=False)
 
     //:: if os.path.exists("%s/custom/%s.java" % (template_dir, msg.name)):
-    //:: include("custom/%s.java" % msg.name, msg=msg)
+    //:: include("custom/%s.java" % msg.name, msg=msg, version=version)
     //:: #endif
 
     //:: if msg.data_members:
@@ -124,7 +131,7 @@
 
                 //
                 //:: if os.path.exists("%s/custom/%s.Builder_normalize_stanza.java" % (template_dir, msg.name)):
-                //:: include("custom/%s.Builder_normalize_stanza.java" % msg.name, msg=msg, has_parent=False)
+                //:: include("custom/%s.Builder_normalize_stanza.java" % msg.name, msg=msg, version=version, has_parent=False)
                 //:: #endif
                 return new ${impl_class}(
                 //:: for i, prop in enumerate(msg.data_members):
@@ -134,7 +141,7 @@
                 );
         }
         //:: if os.path.exists("%s/custom/%s.Builder.java" % (template_dir, msg.name)):
-        //:: include("custom/%s.Builder.java" % msg.name, msg=msg, has_parent=True)
+        //:: include("custom/%s.Builder.java" % msg.name, msg=msg, version=version, has_parent=True)
         //:: #endif
 
     }
@@ -164,7 +171,7 @@
             //:: #endfor
 
             //:: if os.path.exists("%s/custom/%s.Builder_normalize_stanza.java" % (template_dir, msg.name)):
-            //:: include("custom/%s.Builder_normalize_stanza.java" % msg.name, msg=msg, has_parent=False)
+            //:: include("custom/%s.Builder_normalize_stanza.java" % msg.name, msg=msg, version=version, has_parent=False)
             //:: #endif
 
             return new ${impl_class}(
@@ -175,7 +182,7 @@
                 );
         }
         //:: if os.path.exists("%s/custom/%s.Builder.java" % (template_dir, msg.name)):
-        //:: include("custom/%s.Builder.java" % msg.name, msg=msg, has_parent=False)
+        //:: include("custom/%s.Builder.java" % msg.name, msg=msg, version=version, has_parent=False)
         //:: #endif
 
     }
@@ -249,7 +256,7 @@
 
             //:: if msg.data_members:
             //:: if os.path.exists("%s/custom/%s.Reader_normalize_stanza.java" % (template_dir, msg.name)):
-            //:: include("custom/%s.Reader_normalize_stanza.java" % msg.name, msg=msg, has_parent=False)
+            //:: include("custom/%s.Reader_normalize_stanza.java" % msg.name, msg=msg, version=version, has_parent=False)
             //:: #endif
             ${impl_class} ${msg.variable_name} = new ${impl_class}(
                     ${",\n                      ".join(
@@ -364,7 +371,7 @@
     }
 
     //:: if os.path.exists("%s/custom/%s_toString.java" % (template_dir, msg.name)):
-    //:: include("custom/%s_toString.java" % msg.name, msg=msg, has_parent=False)
+    //:: include("custom/%s_toString.java" % msg.name, msg=msg, version=version, has_parent=False)
     //:: else:
     @Override
     public String toString() {
diff --git a/java_gen/templates/of_factories.java b/java_gen/templates/of_factories.java
index f9ec015..ba73286 100644
--- a/java_gen/templates/of_factories.java
+++ b/java_gen/templates/of_factories.java
@@ -51,7 +51,9 @@
 
     private static class GenericReader implements OFMessageReader<OFMessage> {
         public OFMessage readFrom(ChannelBuffer bb) throws OFParseError {
-            short wireVersion = U8.f(bb.getByte(0));
+            if(!bb.readable())
+                return null;
+            short wireVersion = U8.f(bb.getByte(bb.readerIndex()));
             OFFactory factory;
             switch (wireVersion) {
             //:: for v in versions:
diff --git a/java_gen/templates/of_interface.java b/java_gen/templates/of_interface.java
index a515ad1..18e2cf1 100644
--- a/java_gen/templates/of_interface.java
+++ b/java_gen/templates/of_interface.java
@@ -27,6 +27,7 @@
 //::
 //:: import itertools
 //:: import re
+//:: import os
 //:: include('_copyright.java')
 
 //:: include('_autogen.java')
@@ -39,6 +40,10 @@
 //:: for prop in msg.members:
     ${prop.java_type.public_type} ${prop.getter_name}()${ "" if prop.is_universal else " throws UnsupportedOperationException"};
 //:: #endfor
+//:: if os.path.exists("%s/custom/interface/%s.java" % (template_dir, msg.name)):
+//:: include("custom/interface/%s.java" % msg.name, msg=msg)
+//:: #endif
+    
 
     void writeTo(ChannelBuffer channelBuffer);