Merge branch 'master' of github.com:andi-bigswitch/loxigen
diff --git a/java_gen/codegen.py b/java_gen/codegen.py
index f3619d4..bc9b9a0 100644
--- a/java_gen/codegen.py
+++ b/java_gen/codegen.py
@@ -37,6 +37,7 @@
 from loxi_ir import *
 import lang_java
 import test_data
+from import_cleaner import ImportCleaner
 
 import loxi_utils.loxi_utils as loxi_utils
 
@@ -84,6 +85,14 @@
         print "filename: %s" % filename
         with open(filename, "w") as f:
             loxi_utils.render_template(f, template, [self.templates_dir], context, prefix=prefix)
+        
+        try:
+            cleaner = ImportCleaner(filename)
+            cleaner.find_used_imports()
+            cleaner.rewrite_file(filename)
+        except:
+            print 'Cannot clean imports from file %s' % filename
+        
 
     def create_of_const_enums(self):
         for enum in self.java_model.enums:
@@ -126,13 +135,16 @@
                 else:
                     print "Class %s ignored by generate_class" % java_class.name
 
-    def create_unit_test(self, unit_test):
-        if unit_test.has_test_data:
-            self.render_class(clazz=unit_test,
-                    template='unit_test.java', src_dir="src/test/java",
-                    version=unit_test.java_class.version,
-                    test=unit_test, msg=unit_test.java_class,
-                    test_data=unit_test.test_data)
+    def create_unit_test(self, unit_tests):
+        if unit_tests.has_test_data:
+            for i in range(unit_tests.length):
+                unit_test = unit_tests.get_test_unit(i)
+                if unit_test.has_test_data:
+                    self.render_class(clazz=unit_test,
+                            template='unit_test.java', src_dir="src/test/java",
+                            version=unit_test.java_class.version,
+                            test=unit_test, msg=unit_test.java_class,
+                            test_data=unit_test.test_data)
 
     def create_of_factories(self):
         factory = self.java_model.of_factory
diff --git a/java_gen/import_cleaner.py b/java_gen/import_cleaner.py
new file mode 100755
index 0000000..83897d4
--- /dev/null
+++ b/java_gen/import_cleaner.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+
+import sys
+import re
+
+class ImportLine:
+    def __init__(self, line):
+        self.line = line
+        class_name = None
+        if line[len(line) - 1] == '*':
+            class_name = '*'
+        else:
+            i = 7
+            while i < len(line) - 1:
+                if re.match('\.[A-Z][\..]*$', line[i - 1 : len(line) - 1]):
+                    class_name = line[i : len(line) - 1]
+                    break
+                i = i + 1
+            if class_name is None:
+                class_name = line[line.rfind('.') + 1 : len(line) - 1]
+        self.class_name = class_name
+
+
+class ImportCleaner:
+    def __init__(self, path):
+        f = open(path)
+        self.imp_lines = []
+        self.code_lines = []
+        self.imports_first_line = -1
+        i = 0
+        for line in f:
+            if len(line) > 6 and re.match('^[ \t]*import ', line):
+                self.imp_lines.append(ImportLine(line.rstrip()))
+                if self.imports_first_line == -1:
+                    self.imports_first_line = i
+            else:
+                self.code_lines.append(line.rstrip())
+            i = i + 1
+        f.close()
+
+    def find_used_imports(self):
+        self.used_imports = []
+        for line in self.code_lines:
+            temp = []
+            for imp in self.imp_lines:
+                if imp.class_name == '*' or line.find(imp.class_name) > -1:
+                    temp.append(imp)
+            for x in temp:
+                self.imp_lines.remove(x)
+                self.used_imports.append(x)
+
+    def rewrite_file(self, path):
+        f = open(path, 'w')
+        imports_written = False
+        for i in range(len(self.code_lines)):
+            if not imports_written and self.imports_first_line == i:
+                # Put all imports
+                for imp in self.used_imports:
+                    f.write(imp.line + '\n')
+                imports_written = True
+            # Put next code line
+            f.write(self.code_lines[i] + '\n')
+        f.close()
+
+def main(argv):
+    if len(argv) != 2:
+        print 'Usage: ImportCleaner <java file>'
+        return
+
+    filename = argv[1]
+    print 'Cleaning imports from file %s' % (filename)
+    cleaner = ImportCleaner(filename)
+    cleaner.find_used_imports()
+    cleaner.rewrite_file(filename)
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index 0eb8f11..66eeacd 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -215,7 +215,7 @@
             self.parent_interface = parent_interface
         else:
             self.parent_interface = None
-
+            
     def class_info(self):
         """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
         # FIXME: This duplicates inheritance information that is now available in the loxi_ir
@@ -330,7 +330,7 @@
     @property
     @memoize
     def unit_test(self):
-        return JavaUnitTest(self)
+        return JavaUnitTestSet(self)
 
     @property
     def name(self):
@@ -595,18 +595,58 @@
 ### Unit Test
 #######################################################################
 
-class JavaUnitTest(object):
+class JavaUnitTestSet(object):
     def __init__(self, java_class):
         self.java_class = java_class
-        self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
+        first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
                                                      name=java_class.c_name[3:])
+        data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
+                                                     name=java_class.c_name[3:]) + "{i}.data"
+        test_class_name = self.java_class.name + "Test"
+        self.test_units = []
+        if test_data.exists(first_data_file_name):
+            self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
+        i = 1
+        while test_data.exists(data_file_template.format(i=i)):
+            self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
+            i = i + 1
+        
+    @property
+    def package(self):
+        return self.java_class.package
+
+    @property
+    def has_test_data(self):
+        return len(self.test_units) > 0
+
+    @property
+    def length(self):
+        return len(self.test_units)
+    
+    def get_test_unit(self, i):
+        return self.test_units[i]
+
+
+class JavaUnitTest(object):
+    def __init__(self, java_class, file_name=None, test_class_name=None):
+        self.java_class = java_class
+        if file_name is None:
+            self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
+                                                         name=java_class.c_name[3:])
+        else:
+            self.data_file_name = file_name
+        if test_class_name is None:
+            self.test_class_name = self.java_class.name + "Test"
+        else:
+            self.test_class_name = test_class_name
+        
     @property
     def package(self):
         return self.java_class.package
 
     @property
     def name(self):
-        return self.java_class.name + "Test"
+        return self.test_class_name
 
     @property
     def has_test_data(self):
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index cc34a57..0c5de0e 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -1,6 +1,6 @@
+import errno
 import loxi_utils.loxi_utils as loxi_utils
 import os
-import errno
 import re
 import subprocess
 import time
@@ -262,6 +262,28 @@
         .op(read="Wildcards.of(bb.readInt())", write="bb.writeInt($name.getInt())");
 transport_port = JType("TransportPort")\
         .op(read="TransportPort.read2Bytes(bb)", write="$name.write2Bytes(bb)")
+eth_type = JType("EthType")\
+        .op(read="EthType.read2Bytes(bb)", write="$name.write2Bytes(bb)")
+vlan_vid = JType("VlanVid")\
+        .op(read="VlanVid.read2Bytes(bb)", write="$name.write2Bytes(bb)")
+vlan_pcp = JType("VlanPcp")\
+        .op(read="VlanPcp.readByte(bb)", write="$name.writeByte(bb)")
+ip_dscp = JType("IpDscp")\
+        .op(read="IpDscp.readByte(bb)", write="$name.writeByte(bb)")
+ip_ecn = JType("IpEcn")\
+        .op(read="IpEcn.readByte(bb)", write="$name.writeByte(bb)")
+ip_proto = JType("IpProtocol")\
+        .op(read="IpProtocol.readByte(bb)", write="$name.writeByte(bb)")
+icmpv4_type = JType("ICMPv4Type")\
+        .op(read="ICMPv4Type.readByte(bb)", write="$name.writeByte(bb)")
+icmpv4_code = JType("ICMPv4Code")\
+        .op(read="ICMPv4Code.readByte(bb)", write="$name.writeByte(bb)")
+arp_op = JType("ArpOpcode")\
+        .op(read="ArpOpcode.read2Bytes(bb)", write="$name.write2Bytes(bb)")
+ipv6_flabel = JType("IPv6FlowLabel")\
+        .op(read="IPv6FlowLabel.read4Bytes(bb)", write="$name.write4Bytes(bb)")
+metadata = JType("OFMetadata")\
+        .op(read="OFMetadata.read8Bytes(bb)", write="$name.write8Bytes(bb)")
 oxm = JType("OFOxm")\
         .op(read="OFOxmVer$version.READER.readFrom(bb)", write="$name.writeTo(bb)")
 meter_features = JType("OFMeterFeatures")\
@@ -300,13 +322,45 @@
 ## Map that defines exceptions from the standard loxi->java mapping scheme
 # map of {<loxi_class_name> : { <loxi_member_name> : <JType instance> } }
 exceptions = {
-        'of_packet_in': {
-            'data' : octets,
-            'reason': packetin_reason
-            },
-        'of_oxm_tcp_src' : {
-            'value' : transport_port
-            },
+        'of_packet_in': { 'data' : octets, 'reason': packetin_reason },
+        'of_oxm_tcp_src' : { 'value' : transport_port },
+        'of_oxm_tcp_src_masked' : { 'value' : transport_port, 'value_mask' : transport_port },
+        'of_oxm_tcp_dst' : { 'value' : transport_port },
+        'of_oxm_tcp_dst_masked' : { 'value' : transport_port, 'value_mask' : transport_port },
+        'of_oxm_udp_src' : { 'value' : transport_port },
+        'of_oxm_udp_src_masked' : { 'value' : transport_port, 'value_mask' : transport_port },
+        'of_oxm_udp_dst' : { 'value' : transport_port },
+        'of_oxm_udp_dst_masked' : { 'value' : transport_port, 'value_mask' : transport_port },
+        'of_oxm_sctp_src' : { 'value' : transport_port },
+        'of_oxm_sctp_src_masked' : { 'value' : transport_port, 'value_mask' : transport_port },
+        'of_oxm_sctp_dst' : { 'value' : transport_port },
+        'of_oxm_sctp_dst_masked' : { 'value' : transport_port, 'value_mask' : transport_port },
+        'of_oxm_eth_type' : { 'value' : eth_type },
+        'of_oxm_eth_type_masked' : { 'value' : eth_type, 'value_mask' : eth_type },
+        'of_oxm_vlan_vid' : { 'value' : vlan_vid },
+        'of_oxm_vlan_vid_masked' : { 'value' : vlan_vid, 'value_mask' : vlan_vid },
+        'of_oxm_vlan_pcp' : { 'value' : vlan_pcp },
+        'of_oxm_vlan_pcp_masked' : { 'value' : vlan_pcp, 'value_mask' : vlan_pcp },
+        'of_oxm_ip_dscp' : { 'value' : ip_dscp },
+        'of_oxm_ip_dscp_masked' : { 'value' : ip_dscp, 'value_mask' : ip_dscp },
+        'of_oxm_ip_ecn' : { 'value' : ip_ecn },
+        'of_oxm_ip_ecn_masked' : { 'value' : ip_ecn, 'value_mask' : ip_ecn },
+        'of_oxm_ip_proto' : { 'value' : ip_proto },
+        'of_oxm_ip_proto_masked' : { 'value' : ip_proto, 'value_mask' : ip_proto },
+        'of_oxm_icmpv4_type' : { 'value' : icmpv4_type },
+        'of_oxm_icmpv4_type_masked' : { 'value' : icmpv4_type, 'value_mask' : icmpv4_type },
+        'of_oxm_icmpv4_code' : { 'value' : icmpv4_code },
+        'of_oxm_icmpv4_code_masked' : { 'value' : icmpv4_code, 'value_mask' : icmpv4_code },
+        'of_oxm_arp_op' : { 'value' : arp_op },
+        'of_oxm_arp_op_masked' : { 'value' : arp_op, 'value_mask' : arp_op },
+        'of_oxm_arp_spa' : { 'value' : ipv4 },
+        'of_oxm_arp_spa_masked' : { 'value' : ipv4, 'value_mask' : ipv4 },
+        'of_oxm_arp_tpa' : { 'value' : ipv4 },
+        'of_oxm_arp_tpa_masked' : { 'value' : ipv4, 'value_mask' : ipv4 },
+        'of_oxm_ipv6_flabel' : { 'value' : ipv6_flabel },
+        'of_oxm_ipv6_flabel_masked' : { 'value' : ipv6_flabel, 'value_mask' : ipv6_flabel },
+        'of_oxm_metadata' : { 'value' : metadata },
+        'of_oxm_metadata_masked' : { 'value' : metadata, 'value_mask' : metadata },
 }
 
 
diff --git a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/Match.java b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/Match.java
index d35f709..8f17845 100644
--- a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/Match.java
+++ b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/Match.java
@@ -4,89 +4,190 @@
 import org.openflow.types.Masked;
 import org.openflow.types.OFValueType;
 
-public interface Match extends OFObject {
-
-    /*
-     * Preconditions
-     * On preconditions (from the OF1.1 spec, page 28, the OF1.0 spec failed to explicitly
-     * specify this, but it is the behavior of Of1.0 switches):
-     * Protocol-specific fields within ofp_match will be ignored within a single table when
-     * the corresponding protocol is not specified in the match. The MPLS match fields will
-     * be ignored unless the Ethertype is specified as MPLS. Likewise, the IP header and
-     * transport header fields will be ignored unless the Ethertype is specified as either
-     * IPv4 or ARP. The tp_src and tp_dst fields will be ignored unless the network protocol
-     * specified is as TCP, UDP or SCTP. Fields that are ignored don�t need to be wildcarded
-     * and should be set to 0.
-     */
+/**
+ * Generic interface for version-agnostic immutable Match structure.
+ * The Match structure is defined in the OpenFlow protocol, and it contains information on
+ * the fields to be matched in a specific flow record.
+ * This interface does not assume anything on the fields in the Match structure. If in
+ * some version, the match structure cannot handle a certain field, it may return <code>false</code>
+ * for <code>supports(...)</code> calls, and throw <code>UnsupportedOperationException</code> from all
+ * other methods in such cases.
+ * <br><br>
+ * On wildcards and masks:<br>
+ * This interface defines the following masking notations for fields:
+ * <ul>
+ * <li><b>Exact</b>: field is matched exactly against a single, fixed value (no mask, or mask is all ones).
+ * <li><b>Wildcarded</b>: field is not being matched. It is fully masked (mask=0) and any value of it
+ * will match the flow record having this match.
+ * <li><b>Partially masked</b>: field is matched using a specified mask which is neither 0 nor all ones. Mask can
+ * be either arbitrary or require some specific structure.
+ * </ul>
+ * Implementing classes may or may not support all types of these masking types. They may also support
+ * them in part. For example, OF1.0 supports exact match and (full) wildcarding for all fields, but it
+ * does only supports partial masking for IP source/destination fields, and this partial masking must be
+ * in the CIDR prefix format. Thus, OF1.0 implementation may throw <code>UnsupportedOperationException</code> if given
+ * in <code>setMaksed</code> an IP mask of, for example, 255.0.255.0, or if <code>setMasked</code> is called for any field
+ * which is not IP source/destination address.
+ * <br><br>
+ * On prerequisites:<br>
+ * From the OF1.1 spec, page 28, the OF1.0 spec failed to explicitly specify this, but it
+ * is the behavior of OF1.0 switches:
+ * "Protocol-specific fields within ofp_match will be ignored within a single table when 
+ * the corresponding protocol is not specified in the match. The MPLS match fields will 
+ * be ignored unless the Ethertype is specified as MPLS. Likewise, the IP header and 
+ * transport header fields will be ignored unless the Ethertype is specified as either 
+ * IPv4 or ARP. The tp_src and tp_dst fields will be ignored unless the network protocol 
+ * specified is as TCP, UDP or SCTP. Fields that are ignored donÕt need to be wildcarded 
+ * and should be set to 0."
+ * <br><br>
+ * This interface uses generics to assure type safety in users code. However, implementing classes may have to suppress 
+ * 'unchecked cast' warnings while making sure they correctly cast base on their implementation details.
+ * 
+ * @author Yotam Harchol (yotam.harchol@bigswitch.com)
+ */
+public interface Match extends OFObject {    
 
     /**
-     * Returns the value for the given field from this match.
-     *
+     * Returns a value for the given field if:
+     * <ul>
+     * <li>Field is supported
+     * <li>Field is not fully wildcarded
+     * <li>Prerequisites are ok
+     * </ul>
+     * If one of the above conditions does not hold, returns null. Value is returned masked if partially wildcarded.
+     * 
      * @param field Match field to retrieve
-     * @return Value of match field
+     * @return Value of match field (may be masked), or <code>null</code> if field is one of the conditions above does not hold.
+     * @throws UnsupportedOperationException If field is not supported.
      */
     public <F extends OFValueType<F>> F get(MatchField<F> field) throws UnsupportedOperationException;
 
     /**
-     * Returns the masked value for the given field from this match.
-     * Precondition: field is partially wildcarded.
-     *
-     * @param field Match field to retrieve
-     * @return Masked value of match field or null if no mask
+     * Returns the masked value for the given field from this match, along with the mask itself.
+     * Prerequisite: field is partially masked.
+     * If prerequisite is not met, a <code>null</code> is returned.
+     * 
+     * @param field Match field to retrieve.
+     * @return Masked value of match field or null if no mask is set.
+     * @throws UnsupportedOperationException If field is not supported.
      */
     public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field) throws UnsupportedOperationException;
 
     /**
-     * Returns true if this match object supports the given match field.
-     *
+     * Returns true if and only if this match object supports the given match field.
+     * 
      * @param field Match field
-     * @return
+     * @return true if field is supported, false otherwise.
      */
     public boolean supports(MatchField<?> field);
 
     /**
-     * true iff field supports a bitmask mask that wildcards part of the field
+     * Returns true if and only if this match object supports partially bitmasking of the given field.
      * (note: not all possible values of this bitmask have to be acceptable)
-     *
-     * @param field Match field
-     * @return
+     * 
+     * @param field Match field.
+     * @return true if field can be partially masked, false otherwise.
+     * @throws UnsupportedOperationException If field is not supported.
      */
-    public boolean supportsMasked(MatchField<?> field);
+    public boolean supportsMasked(MatchField<?> field) throws UnsupportedOperationException;
 
     /**
-     * True iff this field is currently fully specified in the match, i.e., the
-     * match will only select packets that match the exact value of getField(field).
-     *
-     * @param field Match field
-     * @return
+     * Returns true if and only if this field is currently specified in the match with an exact value and
+     * no mask. I.e., the specified match will only select packets that match the exact value of getValue(field).
+     * 
+     * @param field Match field.
+     * @return true if field has a specific exact value, false if not.
+     * @throws UnsupportedOperationException If field is not supported.
      */
-    public boolean isExact(MatchField<?> field);
+    public boolean isExact(MatchField<?> field) throws UnsupportedOperationException;
 
     /**
-     * True if this field is currently logically unspecified in the match, i.e, the
-     * value returned by getValue(f) has no impact on whether a packet will be selected
+     * True if and only if this field is currently logically unspecified in the match, i.e, the 
+     * value returned by getValue(f) has no impact on whether a packet will be selected 
      * by the match or not.
-     *
-     * @param field
-     * @return
+     * 
+     * @param field Match field.
+     * @return true if field is fully wildcarded, false if not.
+     * @throws UnsupportedOperationException If field is not supported.
      */
-    public boolean isFullyWildcarded(MatchField<?> field);
+    public boolean isFullyWildcarded(MatchField<?> field) throws UnsupportedOperationException;
 
     /**
-     * True if this field is currently partially specified in the match, i.e, the
-     * match will select packets that match (p.value & getMask(field)) == getValue(field).
-     *
-     * @param field
-     * @return
+     * True if and only if this field is currently partially specified in the match, i.e, the 
+     * match will only select packets that match (p.value & getMask(field)) == getValue(field),
+     * and getMask(field) != 0.
+     * 
+     * @param field Match field.
+     * @return true if field is partially masked, false if not.
+     * @throws UnsupportedOperationException If field is not supported.
      */
-    public boolean isPartiallyMasked(MatchField<?> field);
-
+    public boolean isPartiallyMasked(MatchField<?> field) throws UnsupportedOperationException;
+    
     /**
      * Returns a builder to build new instances of this type of match object.
      * @return Match builder
      */
-    public MatchBuilder createBuilder();
+    public Builder createBuilder();
 
-    interface Builder extends MatchBuilder {
+    /**
+     * Builder interface for Match objects.
+     * Builder is used to create new Match objects and it creates the match according to the version it
+     * corresponds to. The builder uses the same notation of wildcards and masks, and can also throw
+     * <code>UnsupportedOperationException</code> if it is asked to create some matching that is not supported in
+     * the version it represents.
+     * 
+     * While used, MatchBuilder may not be consistent in terms of field prerequisites. However, user must
+     * solve these before using the generated Match object as these prerequisites should be enforced in the
+     * getters. 
+     * 
+     * @author Yotam Harchol (yotam.harchol@bigswitch.com)
+     */
+    interface Builder extends Match {
+        /**
+         * Sets a specific exact value for a field.
+         * 
+         * @param field Match field to set.
+         * @param value Value of match field.
+         * @return the Builder instance used.
+         * @throws UnsupportedOperationException If field is not supported.
+         */
+        public <F extends OFValueType<F>> Builder setExact(MatchField<F> field, F value) throws UnsupportedOperationException;
+
+        /**
+         * Sets a masked value for a field.
+         * 
+         * @param field Match field to set.
+         * @param value Value of field.
+         * @param mask Mask value.
+         * @return the Builder instance used.
+         * @throws UnsupportedOperationException If field is not supported, if field is supported but does not support masking, or if mask structure is not supported.
+         */
+        public <F extends OFValueType<F>> Builder setMasked(MatchField<F> field, F value, F mask) throws UnsupportedOperationException;
+
+        /**
+         * Sets a masked value for a field.
+         * 
+         * @param field Match field to set.
+         * @param valueWithMask Compound Masked object contains the value and the mask.
+         * @return the Builder instance used.
+         * @throws UnsupportedOperationException If field is not supported, if field is supported but does not support masking, or if mask structure is not supported.
+         */
+        public <F extends OFValueType<F>> Builder setMasked(MatchField<F> field, Masked<F> valueWithMask) throws UnsupportedOperationException;
+
+        /**
+         * Unsets any value given for the field and wildcards it so that it matches any value.
+         * 
+         * @param field Match field to unset.
+         * @return the Builder instance used.
+         * @throws UnsupportedOperationException If field is not supported.
+         */
+        public <F extends OFValueType<F>> Builder wildcard(MatchField<F> field) throws UnsupportedOperationException;
+
+        /**
+         * Returns the match created by this builder.
+         * 
+         * @return a Match object.
+         */
+        public Match getMatch();
     }
-}
+}
\ No newline at end of file
diff --git a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchBuilder.java b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchBuilder.java
deleted file mode 100644
index be7b8b0..0000000
--- a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchBuilder.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.openflow.protocol.match;
-
-import org.openflow.types.Masked;
-import org.openflow.types.OFValueType;
-
-public interface MatchBuilder {
-    /**
-     * Returns the value for the given field from this match.
-     *
-     * @param field Match field to retrieve
-     * @return Value of match field
-     */
-    public <F extends OFValueType<F>> F get(MatchField<F> field) throws UnsupportedOperationException;
-
-    /**
-     * Returns the masked value for the given field from this match.
-     * Precondition: field is partially wildcarded.
-     *
-     * @param field Match field to retrieve
-     * @return Masked value of match field or null if no mask
-     */
-    public <F extends OFValueType<F>> Masked<F> getMasked(MatchField<F> field) throws UnsupportedOperationException;
-
-    /**
-     * Returns true if this match object supports the given match field.
-     *
-     * @param field Match field
-     * @return
-     */
-    public boolean supports(MatchField<?> field);
-
-    /**
-     * true iff field supports a bitmask mask that wildcards part of the field
-     * (note: not all possible values of this bitmask have to be acceptable)
-     *
-     * @param field Match field
-     * @return
-     */
-    public boolean supportsMasked(MatchField<?> field);
-
-    /**
-     * True iff this field is currently fully specified in the match, i.e., the
-     * match will only select packets that match the exact value of getField(field).
-     *
-     * @param field Match field
-     * @return
-     */
-    public boolean isExact(MatchField<?> field);
-
-    /**
-     * True if this field is currently logically unspecified in the match, i.e, the
-     * value returned by getValue(f) has no impact on whether a packet will be selected
-     * by the match or not.
-     *
-     * @param field
-     * @return
-     */
-    public boolean isFullyWildcarded(MatchField<?> field);
-
-    /**
-     * True if this field is currently partially specified in the match, i.e, the
-     * match will select packets that match (p.value & getMask(field)) == getValue(field).
-     *
-     * @param field
-     * @return
-     */
-    public boolean isPartiallyMasked(MatchField<?> field);
-
-
-    public <F extends OFValueType<F>> MatchBuilder setExact(MatchField<F> field, F value);
-
-    public <F extends OFValueType<F>> MatchBuilder setMasked(MatchField<F> field, F value, F mask);
-
-    public <F extends OFValueType<F>> MatchBuilder setMasked(MatchField<F> field, Masked<F> valueWithMask);
-
-    public <F extends OFValueType<F>> MatchBuilder wildcard(MatchField<F> field);
-
-    public Match getMatch();
-}
diff --git a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchField.java b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchField.java
index 4d4935c..5913658 100644
--- a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchField.java
+++ b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchField.java
@@ -11,30 +11,38 @@
 import org.openflow.types.IpEcn;
 import org.openflow.types.IpProtocol;
 import org.openflow.types.MacAddress;
+import org.openflow.types.OFMetadata;
 import org.openflow.types.OFPort;
 import org.openflow.types.OFValueType;
 import org.openflow.types.TransportPort;
 import org.openflow.types.VlanPcp;
 import org.openflow.types.VlanVid;
 
+@SuppressWarnings("unchecked")
 public class MatchField<F extends OFValueType<F>> {
     private final String name;
     public final MatchFields id;
+    private final Prerequisite<?>[] prerequisites;
 
-    private MatchField(final String name, final MatchFields id) {
+    private MatchField(final String name, final MatchFields id, Prerequisite<?>... prerequisites) {
         this.name = name;
         this.id = id;
+        this.prerequisites = prerequisites;
     }
 
     public final static MatchField<OFPort> IN_PORT =
             new MatchField<OFPort>("in_port", MatchFields.IN_PORT);
+    
     public final static MatchField<OFPort> IN_PHY_PORT =
-            new MatchField<OFPort>("in_phy_port", MatchFields.PHYSICAL_PORT);
-    public final static MatchField<OFPort> METADATA =
-            new MatchField<OFPort>("metadata", MatchFields.METADATA);
+            new MatchField<OFPort>("in_phy_port", MatchFields.PHYSICAL_PORT,
+                    new Prerequisite<OFPort>(MatchField.IN_PORT));
+    
+    public final static MatchField<OFMetadata> METADATA =
+            new MatchField<OFMetadata>("metadata", MatchFields.METADATA);
 
     public final static MatchField<MacAddress> ETH_DST =
             new MatchField<MacAddress>("eth_dst", MatchFields.ETH_DST);
+    
     public final static MatchField<MacAddress> ETH_SRC =
             new MatchField<MacAddress>("eth_src", MatchFields.ETH_SRC);
 
@@ -43,63 +51,106 @@
     
     public final static MatchField<VlanVid> VLAN_VID =
             new MatchField<VlanVid>("vlan_vid", MatchFields.VLAN_VID);
-    public final static MatchField<VlanPcp> VLAN_PCP =
-            new MatchField<VlanPcp>("vlan_pcp", MatchFields.VLAN_PCP);
     
+    public final static MatchField<VlanPcp> VLAN_PCP =
+            new MatchField<VlanPcp>("vlan_pcp", MatchFields.VLAN_PCP,
+                    new Prerequisite<VlanVid>(MatchField.VLAN_VID));
     
     public final static MatchField<IpDscp> IP_DSCP =
-            new MatchField<IpDscp>("ip_dscp", MatchFields.IP_DSCP);
+            new MatchField<IpDscp>("ip_dscp", MatchFields.IP_DSCP,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_IPv4, EthType.ETH_TYPE_IPv6));
+    
     public final static MatchField<IpEcn> IP_ECN =
-            new MatchField<IpEcn>("ip_dscp", MatchFields.IP_ECN);
+            new MatchField<IpEcn>("ip_dscp", MatchFields.IP_ECN,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_IPv4, EthType.ETH_TYPE_IPv6));
+    
     public final static MatchField<IpProtocol> IP_PROTO =
-            new MatchField<IpProtocol>("ip_proto", MatchFields.IP_PROTO);
+            new MatchField<IpProtocol>("ip_proto", MatchFields.IP_PROTO,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_IPv4, EthType.ETH_TYPE_IPv6));
 
     public final static MatchField<IPv4> IPV4_SRC =
-            new MatchField<IPv4>("ipv4_src", MatchFields.IPV4_SRC);
+            new MatchField<IPv4>("ipv4_src", MatchFields.IPV4_SRC,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_IPv4));
+    
     public final static MatchField<IPv4> IPV4_DST =
-            new MatchField<IPv4>("ipv4_dst", MatchFields.IPV4_DST);
+            new MatchField<IPv4>("ipv4_dst", MatchFields.IPV4_DST,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_IPv4));
 
     public final static MatchField<TransportPort> TCP_SRC = new MatchField<TransportPort>(
-            "tcp_src", MatchFields.TCP_SRC);
+            "tcp_src", MatchFields.TCP_SRC,
+            new Prerequisite<IpProtocol>(MatchField.IP_PROTO, IpProtocol.IP_PROTO_TCP));
+    
     public final static MatchField<TransportPort> TCP_DST = new MatchField<TransportPort>(
-            "tcp_dst", MatchFields.TCP_DST);
+            "tcp_dst", MatchFields.TCP_DST,
+            new Prerequisite<IpProtocol>(MatchField.IP_PROTO, IpProtocol.IP_PROTO_TCP));
 
     public final static MatchField<TransportPort> UDP_SRC = new MatchField<TransportPort>(
-            "udp_src", MatchFields.UDP_SRC);
+            "udp_src", MatchFields.UDP_SRC,
+            new Prerequisite<IpProtocol>(MatchField.IP_PROTO, IpProtocol.IP_PROTO_UDP));
+    
     public final static MatchField<TransportPort> UDP_DST = new MatchField<TransportPort>(
-            "udp_dst", MatchFields.UDP_DST);
+            "udp_dst", MatchFields.UDP_DST,
+            new Prerequisite<IpProtocol>(MatchField.IP_PROTO, IpProtocol.IP_PROTO_UDP));
 
     public final static MatchField<TransportPort> SCTP_SRC = new MatchField<TransportPort>(
-            "sctp_src", MatchFields.SCTP_SRC);
+            "sctp_src", MatchFields.SCTP_SRC,
+            new Prerequisite<IpProtocol>(MatchField.IP_PROTO, IpProtocol.IP_PROTO_SCTP));
+    
     public final static MatchField<TransportPort> SCTP_DST = new MatchField<TransportPort>(
-            "sctp_dst", MatchFields.SCTP_DST);
+            "sctp_dst", MatchFields.SCTP_DST,
+            new Prerequisite<IpProtocol>(MatchField.IP_PROTO, IpProtocol.IP_PROTO_SCTP));
 
     public final static MatchField<ICMPv4Type> ICMPV4_TYPE = new MatchField<ICMPv4Type>(
-            "icmpv4_src", MatchFields.ICMPV4_TYPE);
+            "icmpv4_src", MatchFields.ICMPV4_TYPE,
+            new Prerequisite<IpProtocol>(MatchField.IP_PROTO, IpProtocol.IP_PROTO_ICMP));
+    
     public final static MatchField<ICMPv4Code> ICMPV4_CODE = new MatchField<ICMPv4Code>(
-            "icmpv4_dst", MatchFields.ICMPV4_CODE);
+            "icmpv4_dst", MatchFields.ICMPV4_CODE,
+            new Prerequisite<IpProtocol>(MatchField.IP_PROTO, IpProtocol.IP_PROTO_ICMP));
 
     public final static MatchField<ArpOpcode> ARP_OP = new MatchField<ArpOpcode>(
-            "arp_op", MatchFields.ARP_OP);
+            "arp_op", MatchFields.ARP_OP,
+            new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_ARP));
+    
     public final static MatchField<IPv4> ARP_SPA =
-            new MatchField<IPv4>("arp_spa", MatchFields.ARP_SPA);
+            new MatchField<IPv4>("arp_spa", MatchFields.ARP_SPA,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_ARP));
+    
     public final static MatchField<IPv4> ARP_TPA =
-            new MatchField<IPv4>("arp_tpa", MatchFields.ARP_TPA);
+            new MatchField<IPv4>("arp_tpa", MatchFields.ARP_TPA,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_ARP));
+    
     public final static MatchField<MacAddress> ARP_SHA =
-            new MatchField<MacAddress>("arp_sha", MatchFields.ARP_SHA);
+            new MatchField<MacAddress>("arp_sha", MatchFields.ARP_SHA,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_ARP));
+    
     public final static MatchField<MacAddress> ARP_THA =
-            new MatchField<MacAddress>("arp_tha", MatchFields.ARP_THA);
+            new MatchField<MacAddress>("arp_tha", MatchFields.ARP_THA,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_ARP));
 
     public final static MatchField<IPv6> IPV6_SRC =
-            new MatchField<IPv6>("ipv6_src", MatchFields.IPV6_SRC);
+            new MatchField<IPv6>("ipv6_src", MatchFields.IPV6_SRC,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_IPv6));
+    
     public final static MatchField<IPv6> IPV6_DST =
-            new MatchField<IPv6>("ipv6_dst", MatchFields.IPV6_DST);
+            new MatchField<IPv6>("ipv6_dst", MatchFields.IPV6_DST,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_IPv6));
 
     public final static MatchField<IPv6FlowLabel> IPV6_FLABEL =
-            new MatchField<IPv6FlowLabel>("ipv6_flabel", MatchFields.IPV6_FLOWLABEL);
+            new MatchField<IPv6FlowLabel>("ipv6_flabel", MatchFields.IPV6_FLOWLABEL,
+                    new Prerequisite<EthType>(MatchField.ETH_TYPE, EthType.ETH_TYPE_IPv6));
 
     public String getName() {
         return name;
     }
+    
+    public boolean arePrerequisitesOK(Match match) {
+        for (Prerequisite<?> p : this.prerequisites) {
+            if (!p.isStaisfied(match)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
 }
diff --git a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchFields.java b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchFields.java
index 21043f2..309f91f 100644
--- a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchFields.java
+++ b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/MatchFields.java
@@ -1,5 +1,6 @@
 package org.openflow.protocol.match;
 
+// MUST BE ORDERED BY THE ORDER OF OF SPEC!!!
 public enum MatchFields {
     IN_PORT,
     PHYSICAL_PORT,
diff --git a/java_gen/pre-written/src/main/java/org/openflow/protocol/match/Prerequisite.java b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/Prerequisite.java
new file mode 100644
index 0000000..f518925
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/openflow/protocol/match/Prerequisite.java
@@ -0,0 +1,44 @@
+package org.openflow.protocol.match;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.openflow.types.OFValueType;
+
+public class Prerequisite<T extends OFValueType<T>> {
+    private MatchField<T> field;
+    private Set<OFValueType<T>> values;
+    private boolean any;
+    
+    public Prerequisite(MatchField<T> field, OFValueType<T>... values) {
+        this.values = new HashSet<OFValueType<T>>();
+        this.field = field;
+        if (values == null || values.length == 0) {
+            this.any = true;
+        } else {
+            this.any = false;
+            for (OFValueType<T> value : values) {
+                this.values.add(value);
+            }
+        }
+    }
+    
+    /**
+     * Returns true if this prerequisite is satisfied by the given match object.
+     * 
+     * @param match Match object
+     * @return true iff prerequisite is satisfied.
+     */
+    public boolean isStaisfied(Match match) {
+        OFValueType<T> res = match.get(this.field);
+        if (res == null)
+            return false;
+        if (this.any)
+            return true;
+        if (this.values.contains(res)) {
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/java_gen/pre-written/src/main/java/org/openflow/types/IPv4WithMask.java b/java_gen/pre-written/src/main/java/org/openflow/types/IPv4WithMask.java
index c24c4fd..0a1a6d3 100644
--- a/java_gen/pre-written/src/main/java/org/openflow/types/IPv4WithMask.java
+++ b/java_gen/pre-written/src/main/java/org/openflow/types/IPv4WithMask.java
@@ -36,7 +36,7 @@
         return res.toString();
     }
     
-    public static OFValueType<?> ofPossiblyMasked(final String string) {
+    public static IPv4WithMask of(final String string) {
         int slashPos;
         String ip = string;
         int maskBits = 0;
@@ -72,7 +72,7 @@
             return IPv4WithMask.of(ipv4, maskAddress);
         } else if (maskBits == 0) {
             // No mask
-            return ipv4;
+            return IPv4WithMask.of(ipv4, IPv4.of(0xFFFFFFFF));
         } else {
             // With mask
             int mask = (-1) << (32 - maskBits);
diff --git a/java_gen/pre-written/src/main/java/org/openflow/types/IPv6WithMask.java b/java_gen/pre-written/src/main/java/org/openflow/types/IPv6WithMask.java
index 72319e3..453507a 100644
--- a/java_gen/pre-written/src/main/java/org/openflow/types/IPv6WithMask.java
+++ b/java_gen/pre-written/src/main/java/org/openflow/types/IPv6WithMask.java
@@ -31,7 +31,7 @@
         return res.toString();
     }
     
-    public static OFValueType<?> ofPossiblyMasked(final String string) {
+    public static IPv6WithMask of(final String string) {
         int slashPos;
         String ip = string;
         int maskBits = 0;
@@ -67,7 +67,7 @@
             return IPv6WithMask.of(ipv6, maskAddress);
         } else if (maskBits == 0) {
             // No mask
-            return ipv6;
+            return IPv6WithMask.of(ipv6, IPv6.of(0xFFFFFFFFFFFFFFFFl, 0xFFFFFFFFFFFFFFFFl));
         } else {
             // With mask
             BigInteger mask = BigInteger.ONE.negate().shiftLeft(128 - maskBits);
diff --git a/java_gen/pre-written/src/main/java/org/openflow/types/OFMetadata.java b/java_gen/pre-written/src/main/java/org/openflow/types/OFMetadata.java
new file mode 100644
index 0000000..83d02da
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/openflow/types/OFMetadata.java
@@ -0,0 +1,34 @@
+package org.openflow.types;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+public class OFMetadata extends U64 implements OFValueType<OFMetadata> {
+    
+    private static int LENGTH = 8;
+
+    protected OFMetadata(long raw) {
+        super(raw);
+    }
+
+    public static OFMetadata of(long raw) {
+        return new OFMetadata(raw);
+    }
+    
+    public static OFMetadata read8Bytes(ChannelBuffer cb) {
+        return OFMetadata.of(cb.readLong());
+    }
+    
+    public void write8Bytes(ChannelBuffer cb) {
+        cb.writeLong(super.getValue());
+    }
+
+    @Override
+    public int getLength() {
+        return LENGTH;
+    }
+
+    @Override
+    public OFMetadata applyMask(OFMetadata mask) {
+        return OFMetadata.of(this.getValue() & mask.getValue());
+    }
+}
diff --git a/java_gen/pre-written/src/main/java/org/openflow/types/U64.java b/java_gen/pre-written/src/main/java/org/openflow/types/U64.java
index 750398f..7871445 100644
--- a/java_gen/pre-written/src/main/java/org/openflow/types/U64.java
+++ b/java_gen/pre-written/src/main/java/org/openflow/types/U64.java
@@ -24,7 +24,7 @@
 
     private final long raw;
 
-    private U64(final long raw) {
+    protected U64(final long raw) {
         this.raw = raw;
     }
 
diff --git a/java_gen/pre-written/src/main/java/org/openflow/types/VlanVid.java b/java_gen/pre-written/src/main/java/org/openflow/types/VlanVid.java
index ca2c3c2..e36a5dd 100644
--- a/java_gen/pre-written/src/main/java/org/openflow/types/VlanVid.java
+++ b/java_gen/pre-written/src/main/java/org/openflow/types/VlanVid.java
@@ -70,7 +70,7 @@
         c.writeShort(this.vid);
     }
 
-    public VlanVid read2Bytes(ChannelBuffer c) throws OFParseError {
+    public static VlanVid read2Bytes(ChannelBuffer c) throws OFParseError {
         return VlanVid.of(c.readShort());
     }
 
diff --git a/java_gen/pre-written/src/test/java/org/openflow/types/IPv4Test.java b/java_gen/pre-written/src/test/java/org/openflow/types/IPv4Test.java
index d344021..a5ac1be 100644
--- a/java_gen/pre-written/src/test/java/org/openflow/types/IPv4Test.java
+++ b/java_gen/pre-written/src/test/java/org/openflow/types/IPv4Test.java
@@ -108,29 +108,23 @@
         }
     }
     
-    @SuppressWarnings("unchecked")
     @Test
-    public void testOfPossiblyMasked() throws OFParseError, OFShortRead {
+    public void testOfMasked() throws OFParseError, OFShortRead {
         for (int i = 0; i < ipsWithMask.length; i++) {
-            OFValueType value = IPv4WithMask.ofPossiblyMasked(ipsWithMask[i]);
-            if (value instanceof IPv4 && !hasMask[i]) {
-                // Types OK, check values
-                IPv4 ip = (IPv4)value;
+            IPv4WithMask value = IPv4WithMask.of(ipsWithMask[i]);
+            if (!hasMask[i]) {
+                IPv4 ip = value.getValue();
                 assertArrayEquals(ipsWithMaskValues[i][0], ip.getBytes());
-            } else if (value instanceof Masked && hasMask[i]) {
-                Masked<IPv4> ip = null;
-                try {
-                    ip = (Masked<IPv4>)value;
-                } catch (ClassCastException e) {
-                    fail("Invalid Masked<T> type.");
+            } else if (hasMask[i]) {
+                byte[] ipBytes = new byte[4];
+                System.arraycopy(ipsWithMaskValues[i][0], 0, ipBytes, 0, 4);
+                assertEquals(ipBytes.length, value.getValue().getBytes().length);
+                for (int j = 0; j < ipBytes.length; j++) {
+                    ipBytes[j] &= ipsWithMaskValues[i][1][j];
                 }
-                // Types OK, check values
-                assertArrayEquals(ipsWithMaskValues[i][0], ip.getValue().getBytes());
-                assertArrayEquals(ipsWithMaskValues[i][1], ip.getMask().getBytes());
-            } else if (value instanceof IPv4) {
-                fail("Expected masked IPv4, got unmasked IPv4.");
-            } else {
-                fail("Expected unmasked IPv4, got masked IPv4.");
+                
+                assertArrayEquals(ipBytes, value.getValue().getBytes());
+                assertArrayEquals(ipsWithMaskValues[i][1], value.getMask().getBytes());
             }
         }
     }
diff --git a/java_gen/pre-written/src/test/java/org/openflow/types/IPv6Test.java b/java_gen/pre-written/src/test/java/org/openflow/types/IPv6Test.java
index f2f6fe8..71ab28e 100644
--- a/java_gen/pre-written/src/test/java/org/openflow/types/IPv6Test.java
+++ b/java_gen/pre-written/src/test/java/org/openflow/types/IPv6Test.java
@@ -58,31 +58,25 @@
     @Test
     public void testMasked() throws UnknownHostException {
         for(int i=0; i < ipsWithMask.length; i++ ) {
-            OFValueType value = IPv6WithMask.ofPossiblyMasked(ipsWithMask[i]);
-            if (value instanceof IPv6 && !hasMask[i]) {
-                // Types OK, check values
-                IPv6 ip = (IPv6)value;
+            IPv6WithMask value = IPv6WithMask.of(ipsWithMask[i]);
+            if (!hasMask[i]) {
+                IPv6 ip = value.getValue();
                 InetAddress inetAddress = InetAddress.getByName(ipsWithMask[i]);
 
                 assertArrayEquals(ip.getBytes(), inetAddress.getAddress());
                 assertEquals(ipsWithMask[i], ip.toString());
             } else if (value instanceof IPv6WithMask && hasMask[i]) {
-                IPv6WithMask ip = null;
-                try {
-                    ip = (IPv6WithMask)value;
-                } catch (ClassCastException e) {
-                    fail("Invalid Masked<T> type.");
-                }
-                // Types OK, check values
                 InetAddress inetAddress = InetAddress.getByName(ipsWithMask[i].substring(0, ipsWithMask[i].indexOf('/')));
 
-                assertArrayEquals(ip.value.getBytes(), inetAddress.getAddress());
-                assertEquals(ipsWithMask[i].substring(0, ipsWithMask[i].indexOf('/')), ip.value.toString());
-                assertArrayEquals(masks[i], ip.mask.getBytes());
-            } else if (value instanceof IPv6) {
-                fail("Expected masked IPv6, got unmasked IPv6.");
-            } else {
-                fail("Expected unmasked IPv6, got masked IPv6.");
+                byte[] address = inetAddress.getAddress();
+                assertEquals(address.length, value.getValue().getBytes().length);
+                
+                for (int j = 0; j < address.length; j++) {
+                    address[j] &= masks[i][j];
+                }
+                
+                assertArrayEquals(value.getValue().getBytes(), address);
+                assertArrayEquals(masks[i], value.getMask().getBytes());
             }
         }
     }