Merge branch 'master' of github.com:andi-bigswitch/loxigen
diff --git a/Makefile b/Makefile
index 146bdc5..c99ae0b 100644
--- a/Makefile
+++ b/Makefile
@@ -79,7 +79,7 @@
 
 clean:
 	rm -rf loxi_output # only delete generated files in the default directory
-	rm -f loxigen.log loxigen-test.log .loxi_ts.c .loxi_ts.python
+	rm -f loxigen.log loxigen-test.log .loxi_ts.c .loxi_ts.python .loxi_ts.java
 
 debug:
 	@echo "LOXI_OUTPUT_DIR=\"${LOXI_OUTPUT_DIR}\""
@@ -107,6 +107,12 @@
 	make -C ${LOXI_OUTPUT_DIR}/locitest
 	${LOXI_OUTPUT_DIR}/locitest/locitest
 
+check-java: java
+	cd ${LOXI_OUTPUT_DIR}/openflowj/ && mvn compile test-compile test
+
+package-java: java
+	cd ${LOXI_OUTPUT_DIR}/openflowj/ && mvn package
+
 pylint:
 	pylint -E ${LOXI_PY_FILES}
 
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index b93b330..66eeacd 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -48,6 +48,8 @@
 class JavaModel(object):
     enum_blacklist = set(("OFDefinitions",))
     enum_entry_blacklist = defaultdict(lambda: set(), OFFlowWildcards=set([ "NW_DST_BITS", "NW_SRC_BITS", "NW_SRC_SHIFT", "NW_DST_SHIFT" ]))
+    # OFUint structs are there for god-knows what in loci. We certainly don't need them.
+    interface_blacklist = set( ("OFUint8", "OFUint32",))
     write_blacklist = defaultdict(lambda: set(), OFOxm=set(('typeLen',)), OFAction=set(('type',)), OFInstruction=set(('type',)), OFFlowMod=set(('command', )))
     virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
 
@@ -74,6 +76,8 @@
         for class_name, version_map in version_map_per_class.items():
             interfaces.append(JavaOFInterface(class_name, version_map))
 
+        interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
+
         return interfaces
 
     @property
@@ -114,6 +118,10 @@
                     members=self.interfaces)
 
     def generate_class(self, clazz):
+        """ return wether or not to generate implementation class clazz.
+            Now true for everything except OFTableModVer10.
+            @param clazz JavaOFClass instance
+        """
         if clazz.interface.name.startswith("OFMatchV"):
             return True
         elif clazz.name == "OFTableModVer10":
@@ -188,10 +196,17 @@
         Version agnostic, in contrast to the loxi_ir python model.
     """
     def __init__(self, c_name, version_map):
+        """"
+        @param c_name: loxi style name (e.g., of_flow_add)
+        @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
+        """
         self.c_name = c_name
         self.version_map = version_map
+        # name: the Java Type name, e.g., OFFlowAdd
         self.name = java_type.name_c_to_caps_camel(c_name) if c_name != "of_header" else "OFMessage"
+        # variable_name name to use for variables of this type. i.e., flowAdd
         self.variable_name = self.name[2].lower() + self.name[3:]
+        # name for use in constants: FLOW_ADD
         self.constant_name = c_name.upper().replace("OF_", "")
 
         pck_suffix, parent_interface = self.class_info()
@@ -202,6 +217,10 @@
             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
+        # model (note, that the loxi model is on versioned classes). Should check/infer the
+        # inheritance information from the versioned lox_ir classes.
         if re.match(r'OF.+StatsRequest$', self.name):
             return ("", "OFStatsRequest")
         elif re.match(r'OF.+StatsReply$', self.name):
@@ -241,6 +260,9 @@
     @property
     @memoize
     def members(self):
+        """return a list of all members to be exposed by this interface. Corresponds to
+           the union of the members of the vesioned classes without length, fieldlength
+           and pads (those are handled automatically during (de)serialization and not exposed"""
         all_versions = []
         member_map = collections.OrderedDict()
 
@@ -258,15 +280,18 @@
     @property
     @memoize
     def is_virtual(self):
+        """ Is this interface virtual. If so, do not generate a builder interface """
         return self.name in model.virtual_interfaces or all(ir_class.virtual for ir_class in self.version_map.values())
 
     @property
     def is_universal(self):
+        """ Is this interface universal, i.e., does it exist in all OF versions? """
         return len(self.all_versions) == len(model.versions)
 
     @property
     @memoize
     def all_versions(self):
+        """ return list of all versions that this interface exists in """
         return self.version_map.keys()
 
     def has_version(self, version):
@@ -289,6 +314,11 @@
         Version specific child of a JavaOFInterface
     """
     def __init__(self, interface, version, ir_class):
+        """
+        @param interface JavaOFInterface instance of the parent interface
+        @param version JavaOFVersion
+        @param ir_class OFClass from loxi_ir
+        """
         self.interface = interface
         self.ir_class = ir_class
         self.c_name = self.ir_class.name
@@ -319,11 +349,13 @@
 
     @property
     def min_length(self):
+        """ @return the minimum wire length of an instance of this class in bytes """
         id_tuple = (self.ir_class.name, self.version.int_version)
         return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
 
     @property
     def is_fixed_length(self):
+        """ true iff this class serializes to a fixed length on the wire """
         return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length
 
     def all_properties(self):
@@ -633,7 +665,11 @@
 class JavaEnum(object):
     def __init__(self, c_name, version_enum_map):
         self.c_name = c_name
-        self.name   = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
+
+        if c_name == "of_stats_types":
+            self.name = "OFStatsType"
+        else:
+            self.name   = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
 
         # Port_features has constants that start with digits
         self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index b11a078..0c5de0e 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -21,8 +21,10 @@
     else:
         return camel
 
-
 java_primitive_types = set("byte char short int long".split(" "))
+
+### info table about java primitive types, for casting literals in the source code
+# { name : (signed?, length_in_bits) }
 java_primitives_info = {
         'byte' : (True, 8),
         'char' : (False, 16),
@@ -31,7 +33,12 @@
         'long' : (True, 64),
 }
 
-def format_primitive_value(t, value):
+def format_primitive_literal(t, value):
+    """ Format a primitive numeric literal for inclusion in the
+        java source code. Takes care of casting the literal
+        apropriately for correct representation despite Java's
+        signed-craziness
+    """
     signed, bits = java_primitives_info[t]
     max = (1 << bits)-1
     if value > max:
@@ -46,7 +53,7 @@
             else:
                 return "(%s) 0x%x" % (t, value)
         else:
-            return "0x%x" % value
+            return "0x%x%s" % (value, "L" if t=="long" else "")
 
 ANY = 0xFFFFFFFFFFFFFFFF
 
@@ -59,32 +66,46 @@
     def __str__(self):
         return "[Version: %d, Read: '%s', Write: '%s']" % (self.version, self.read, self.write)
 
+### FIXME: This class should really be cleaned up
 class JType(object):
-    """ Wrapper class to hold C to Java type conversion information """
-    def __init__(self, pub_type, priv_type=None, size=None, read_op=None, write_op=None):
+    """ Wrapper class to hold C to Java type conversion information. JTypes can have a 'public'
+        and or 'private' java type associated with them and can define how those types can be
+        read from and written to ChannelBuffers.
+
+    """
+    def __init__(self, pub_type, priv_type=None, read_op=None, write_op=None):
         self.pub_type = pub_type    # the type we expose externally, e.g. 'U8'
         if priv_type is None:
             priv_type = pub_type
         self.priv_type = priv_type  # the internal storage type
-        self.size = size            # bytes on the wire; None == variable length or hard to calc
         self.ops = {}
-#        if read_op is None:
-#            read_op = 'ChannelUtilsVer$version.read%s(bb)' % self.pub_type
-#        if write_op is None:
-#            write_op = 'ChannelUtilsVer$version.write%s(bb, $name)'  % self.pub_type
-#        self._read_op = read_op
-#        self._write_op = write_op
 
     def op(self, version=ANY, read=None, write=None, pub_type=ANY):
+        """
+        define operations to be performed for reading and writing this type
+        (when read_op, write_op is called). The operations 'read' and 'write'
+        can either be strings ($name, and $version and $length will be replaced),
+        or callables (name, version and length) will be passed.
+
+        @param version int      OF version to define operation for, or ANY for all
+        @param pub_type boolean whether to define operations for the public type (True), the
+                                private type(False) or both (ALL)
+        @param read read expression (either string or callable)s
+        @param write write expression (either string or callable)
+        """
+
         pub_types = [ pub_type ] if pub_type is not ANY else [ False, True ]
         for pub_type in pub_types:
-            self.ops[(version,pub_type)] = VersionOp(version, read, write)
+            self.ops[(version, pub_type)] = VersionOp(version, read, write)
         return self
 
     def format_value(self, value, pub_type=True):
+        # Format a constant value of this type, for inclusion in the java source code
+        # For primitive types, takes care of casting the value appropriately, to
+        # cope with java's signedness limitation
         t = self.pub_type if pub_type else self.priv_type
         if t in java_primitive_types:
-            return format_primitive_value(t, value)
+            return format_primitive_literal(t, value)
         else:
             return value
 
@@ -102,7 +123,18 @@
         return self.pub_type != self.priv_type
 
     def read_op(self, version=None, length=None, pub_type=True):
+        """ return a Java stanza that reads a value of this JType from ChannelBuffer bb.
+        @param version int - OF wire version to generate expression for
+        @param pub_type boolean use this JTypes 'public' (True), or private (False) representation
+        @param length string, for operations that need it (e.g., read a list of unknown length)
+               Java expression evaluating to the byte length to be read. Defaults to the remainig
+               length of the message.
+        @return string containing generated Java expression.
+        """
         if length is None:
+             # assumes that
+             # (1) length of the message has been read to 'length'
+             # (2) readerIndex at the start of the message has been stored in 'start'
             length = "length - (bb.readerIndex() - start)";
 
         ver = ANY if version is None else version.int_version
@@ -119,6 +151,13 @@
             return _read_op.replace("$length", str(length)).replace("$version", version.of_version)
 
     def write_op(self, version=None, name=None, pub_type=True):
+        """ return a Java stanza that writes a value of this JType contained in Java expression
+        'name' to ChannelBuffer bb.
+        @param name string containing Java expression that evaluations to the value to be written
+        @param version int - OF wire version to generate expression for
+        @param pub_type boolean use this JTypes 'public' (True), or private (False) representation
+        @return string containing generated Java expression.
+        """
         ver = ANY if version is None else version.int_version
         _write_op = None
         if (ver, pub_type) in self.ops:
@@ -126,6 +165,7 @@
         elif (ANY, pub_type) in self.ops:
             _write_op = self.ops[(ANY, pub_type)].write
         if _write_op is None:
+
             _write_op = 'ChannelUtilsVer$version.write%s(bb, $name)' % self.pub_type
         if callable(_write_op):
             return _write_op(version, name)
@@ -133,82 +173,81 @@
             return _write_op.replace("$name", str(name)).replace("$version", version.of_version)
 
     def skip_op(self, version=None, length=None):
+        """ return a java stanza that skips an instance of JType in the input ChannelBuffer 'bb'.
+            This is used in the Reader implementations for virtual classes (because after the
+            discriminator field, the concrete Reader instance will re-read all the fields)
+            Currently just delegates to read_op + throws away the result."""
         return self.read_op(version, length)
 
     @property
     def is_primitive(self):
+        """ return true if the pub_type is a java primitive type (and thus needs
+        special treatment, because it doesn't have methods)"""
         return self.pub_type in java_primitive_types
 
     @property
     def is_array(self):
+        """ return true iff the pub_type is a Java array (and thus requires special
+        treament for equals / toString etc.) """
         return self.pub_type.endswith("[]")
 
 
-u8 =  JType('byte',  size=1) \
+##### Predefined JType mappings
+# FIXME: This list needs to be pruned / cleaned up. Most of these are schematic.
+
+u8 =  JType('short', 'byte') \
         .op(read='bb.readByte()', write='bb.writeByte($name)')
-u8_list =  JType('List<U8>',  size=1) \
+u8_list =  JType('List<U8>') \
         .op(read='ChannelUtils.readList(bb, $length, U8.READER)', write='ChannelUtils.writeList(bb, $name)')
-u16 = JType('int', 'int', size=2) \
-        .op(read='U16.f(bb.readShort())', write='bb.writeShort(U16.t($name))')
-u32 = JType('int', 'int', size=4) \
-        .op(read='bb.readInt()', write='bb.writeInt($name)')
-u32_list = JType('List<U32>', 'int[]', size=4) \
+u16 = JType('int', 'short') \
+        .op(read='U16.f(bb.readShort())', write='bb.writeShort(U16.t($name))', pub_type=True) \
+        .op(read='bb.readShort()', write='bb.writeShort($name)', pub_type=False)
+u32 = JType('long', 'int') \
+        .op(read='U32.f(bb.readInt())', write='bb.writeInt(U32.t($name))', pub_type=True) \
+        .op(read='bb.readInt()', write='bb.writeInt($name)', pub_type=False)
+u32_list = JType('List<U32>', 'int[]') \
         .op(read='ChannelUtils.readList(bb, $length, U32.READER)', write='ChannelUtils.writeList(bb, $name)')
-u64 = JType('U64', 'U64', size=8) \
+u64 = JType('U64', 'U64') \
         .op(read='U64.of(bb.readLong())', write='bb.writeLong($name.getValue())')
 of_port = JType("OFPort") \
          .op(version=1, read="OFPort.read2Bytes(bb)", write="$name.write2Bytes(bb)") \
          .op(version=ANY, read="OFPort.read4Bytes(bb)", write="$name.write4Bytes(bb)")
-one_byte_array = JType('byte[]', size=1) \
-        .op(read='ChannelUtils.readBytes(bb, 1)', write='bb.writeBytes($name)')
-two_byte_array = JType('byte[]', size=2) \
-        .op(read='ChannelUtils.readBytes(bb, 2)', write='bb.writeBytes($name)')
-three_byte_array = JType('byte[]', size=3) \
-        .op(read='ChannelUtils.readBytes(bb, 3)', write='bb.writeBytes($name)')
-four_byte_array = JType('byte[]', size=4) \
-        .op(read='ChannelUtils.readBytes(bb, 4)', write='bb.writeBytes($name)')
-five_byte_array = JType('byte[]', size=5) \
-        .op(read='ChannelUtils.readBytes(bb, 5)', write='bb.writeBytes($name)')
-six_byte_array = JType('byte[]', size=6) \
-        .op(read='ChannelUtils.readBytes(bb, 6)', write='bb.writeBytes($name)')
-seven_byte_array = JType('byte[]', size=7) \
-        .op(read='ChannelUtils.readBytes(bb, 7)', write='bb.writeBytes($name)')
 actions_list = JType('List<OFAction>') \
         .op(read='ChannelUtils.readList(bb, $length, OFActionVer$version.READER)', write='ChannelUtils.writeList(bb, $name);')
 instructions_list = JType('List<OFInstruction>') \
         .op(read='ChannelUtils.readList(bb, $length, OFInstructionVer$version.READER)', \
             write='ChannelUtils.writeList(bb, $name)')
-buckets_list = JType('List<OFBucket>', size='ChannelUtilsVer$version.calcListSize($name)') \
+buckets_list = JType('List<OFBucket>') \
         .op(read='ChannelUtils.readList(bb, $length, OFBucketVer$version.READER)', write='ChannelUtils.writeList(bb, $name)')
-port_desc_list = JType('List<OFPhysicalPort>', size='ChannelUtilsVer$version.calcListSize($name)') \
+port_desc_list = JType('List<OFPhysicalPort>') \
         .op(read='ChannelUtils.readList(bb, $length, OFPhysicalPort.READER)', write='ChannelUtils.writeList(bb, $name)')
-port_desc = JType('OFPortDesc', size='$name.getLength()') \
+port_desc = JType('OFPortDesc') \
         .op(read='OFPortDescVer$version.READER.readFrom(bb)', \
             write='$name.writeTo(bb)')
-packet_queue_list = JType('List<OFPacketQueue>', size='ChannelUtilsVer$version.calcListSize($name)') \
+packet_queue_list = JType('List<OFPacketQueue>') \
         .op(read='ChannelUtils.readList(bb, $length, OFPacketQueueVer$version.READER)', write='ChannelUtils.writeList(bb, $name);')
-octets = JType('byte[]', size="$length") \
+octets = JType('byte[]') \
         .op(read='ChannelUtils.readBytes(bb, $length)', \
             write='bb.writeBytes($name)')
-of_match = JType('Match', size="$name.getLength()") \
+of_match = JType('Match') \
         .op(read='ChannelUtilsVer$version.readOFMatch(bb)', \
             write='$name.writeTo(bb)');
-flow_mod_cmd = JType('OFFlowModCommand', 'short', size="$name.getLength()") \
+flow_mod_cmd = JType('OFFlowModCommand', 'short') \
         .op(version=1, read="bb.readShort()", write="bb.writeShort($name)") \
         .op(version=ANY, read="bb.readByte()", write="bb.writeByte($name)")
-mac_addr = JType('MacAddress', 'byte[]', size=6) \
+mac_addr = JType('MacAddress') \
         .op(read="MacAddress.read6Bytes(bb)", \
             write="$name.write6Bytes(bb)")
-port_name = JType('String', size=16) \
+port_name = JType('String') \
         .op(read='ChannelUtils.readFixedLengthString(bb, 16)', \
             write='ChannelUtils.writeFixedLengthString(bb, $name, 16)')
-desc_str = JType('String', size=256) \
+desc_str = JType('String') \
         .op(read='ChannelUtils.readFixedLengthString(bb, 256)', \
             write='ChannelUtils.writeFixedLengthString(bb, $name, 256)')
-serial_num = JType('String', size=32) \
+serial_num = JType('String') \
         .op(read='ChannelUtils.readFixedLengthString(bb, 32)', \
             write='ChannelUtils.writeFixedLengthString(bb, $name, 32)')
-table_name = JType('String', size=32) \
+table_name = JType('String') \
         .op(read='ChannelUtils.readFixedLengthString(bb, 32)', \
             write='ChannelUtils.writeFixedLengthString(bb, $name, 32)')
 ipv4 = JType("IPv4") \
@@ -256,13 +295,6 @@
         'uint16_t' : u16,
         'uint32_t' : u32,
         'uint64_t' : u64,
-        'uint8_t[1]' : one_byte_array,
-        'uint8_t[2]' : two_byte_array,
-        'uint8_t[3]' : three_byte_array,
-        'uint8_t[4]' : four_byte_array,
-        'uint8_t[5]' : five_byte_array,
-        'uint8_t[6]' : six_byte_array,
-        'uint8_t[7]' : seven_byte_array,
         'of_port_no_t' : of_port,
         'list(of_action_t)' : actions_list,
         'list(of_instruction_t)' : instructions_list,
@@ -287,7 +319,8 @@
         'of_meter_features_t': meter_features,
         }
 
-## This is where we drop in special case handling for certain types
+## 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 },
@@ -331,40 +364,40 @@
 }
 
 
-enum_wire_types = {
-        "uint8_t": JType("byte").op(read="bb.readByte()", write="bb.writeByte($name)"),
-        "uint16_t": JType("short").op(read="bb.readShort()", write="bb.writeShort($name)"),
-        "uint32_t": JType("int").op(read="bb.readInt()", write="bb.writeInt($name)"),
-        "uint64_t": JType("long").op(read="bb.readLong()", write="bb.writeLong($name)"),
-}
-
-def convert_enum_wire_type_to_jtype(wire_type):
-    return enum_wire_types[wire_type]
-
+# Create a default mapping for a list type. Type defauls to List<${java_mapping_of_name}>
 def make_standard_list_jtype(c_type):
     m = re.match(r'list\(of_([a-zA-Z_]+)_t\)', c_type)
     if not m:
         raise Exception("Not a recgonized standard list type declaration: %s" % c_type)
     base_name = m.group(1)
     java_base_name = name_c_to_caps_camel(base_name)
+
+    # read op assumes the class has a public final static field READER that implements
+    # OFMessageReader<$class> i.e., can deserialize an instance of class from a ChannelBuffer
+    # write op assumes class implements Writeable
     return JType("List<OF%s>" % java_base_name) \
-        .op(read= 'ChannelUtils.readList(bb, $length, OF%sVer$version.READER)' % java_base_name, \
+        .op(
+            read= 'ChannelUtils.readList(bb, $length, OF%sVer$version.READER)' % java_base_name, \
             write='ChannelUtils.writeList(bb, $name)')
 
+
+#### main entry point for conversion of LOXI types (c_types) Java types.
+# FIXME: This badly needs a refactoring
+
 def convert_to_jtype(obj_name, field_name, c_type):
     """ Convert from a C type ("uint_32") to a java type ("U32")
     and return a JType object with the size, internal type, and marshalling functions"""
     if obj_name in exceptions and field_name in exceptions[obj_name]:
         return exceptions[obj_name][field_name]
     elif ( obj_name == "of_header" or loxi_utils.class_is_message(obj_name)) and field_name == "type" and c_type == "uint8_t":
-        return JType("OFType", 'byte', size=1) \
+        return JType("OFType", 'byte') \
             .op(read='bb.readByte()', write='bb.writeByte($name)')
     elif field_name == "type" and re.match(r'of_action.*', obj_name):
-        return JType("OFActionType", 'short', size=2) \
+        return JType("OFActionType", 'short') \
             .op(read='bb.readShort()', write='bb.writeShort($name)', pub_type=False)\
             .op(read="OFActionTypeSerializerVer$version.readFrom(bb)", write="OFActionTypeSerializerVer$version.writeTo(bb, $name)", pub_type=True)
     elif field_name == "version" and c_type == "uint8_t":
-        return JType("OFVersion", 'byte', size=1) \
+        return JType("OFVersion", 'byte') \
             .op(read='bb.readByte()', write='bb.writeByte($name)')
     elif c_type in default_mtype_to_jtype_convert_map:
         return default_mtype_to_jtype_convert_map[c_type]
@@ -374,3 +407,15 @@
         print "WARN: Couldn't find java type conversion for '%s' in %s:%s" % (c_type, obj_name, field_name)
         jtype = name_c_to_caps_camel(re.sub(r'_t$', "", c_type))
         return JType(jtype)
+
+
+#### Enum specific wiretype definitions
+enum_wire_types = {
+        "uint8_t": JType("byte").op(read="bb.readByte()", write="bb.writeByte($name)"),
+        "uint16_t": JType("short").op(read="bb.readShort()", write="bb.writeShort($name)"),
+        "uint32_t": JType("int").op(read="bb.readInt()", write="bb.writeInt($name)"),
+        "uint64_t": JType("long").op(read="bb.readLong()", write="bb.writeLong($name)"),
+}
+
+def convert_enum_wire_type_to_jtype(wire_type):
+    return enum_wire_types[wire_type]
diff --git a/java_gen/templates/of_class.java b/java_gen/templates/of_class.java
index 0781dcf..43d9893 100644
--- a/java_gen/templates/of_class.java
+++ b/java_gen/templates/of_class.java
@@ -187,7 +187,7 @@
             bb.writeZero(${prop.length});
 //:: elif prop.is_fixed_value:
             // fixed value property ${prop.name} = ${prop.value}
-            ${prop.java_type.write_op(version, prop.value, pub_type=False)};
+            ${prop.java_type.write_op(version, prop.priv_value, pub_type=False)};
 //:: elif prop.is_length_value:
             // ${prop.name} is length of variable message, will be updated at the end
 //:: if not msg.is_fixed_length:
@@ -273,7 +273,9 @@
         int result = 1;
 
         //:: for prop in msg.data_members:
-        //:: if prop.java_type.is_primitive:
+        //:: if prop.java_type.pub_type == 'long':
+        result = prime *  (int) (${prop.name} ^ (${prop.name} >>> 32));
+        //:: elif prop.java_type.is_primitive:
         result = prime * result + ${prop.name};
         //:: elif prop.java_type.is_array:
         result = prime * result + Arrays.hashCode(${prop.name});
diff --git a/java_gen/templates/of_factory_class.java b/java_gen/templates/of_factory_class.java
index 8620787..fec0bcf 100644
--- a/java_gen/templates/of_factory_class.java
+++ b/java_gen/templates/of_factory_class.java
@@ -52,5 +52,9 @@
 //:: #endif
 //:: #endfor
 
+    public OFMessageReader<OFMessage> getMessageReader() {
+        return OFMessageVer${factory.version.of_version}.READER;
+    }
+
     //:: include("_singleton.java", msg=factory)
 }
diff --git a/java_gen/templates/of_factory_interface.java b/java_gen/templates/of_factory_interface.java
index 467504e..39432a8 100644
--- a/java_gen/templates/of_factory_interface.java
+++ b/java_gen/templates/of_factory_interface.java
@@ -42,4 +42,6 @@
     //:: #endif
     ${i.name}.Builder create${i.name[2:]}Builder()${ "" if i.is_universal else " throws UnsupportedOperationException"};
 //:: #endfor
+
+    OFMessageReader<OFMessage> getMessageReader();
 }
diff --git a/test_data/of10/packet_in.data b/test_data/of10/packet_in.data
index 2cd98b0..bd89cf8 100644
--- a/test_data/of10/packet_in.data
+++ b/test_data/of10/packet_in.data
@@ -30,7 +30,7 @@
 -- java
 builder
    .setXid(0x12345678)
-   .setBufferId(0xabcdef01)
+   .setBufferId(0xabcdef01L)
    .setTotalLen(9)
    .setInPort(OFPort.LOCAL)
    .setReason(OFPacketInReason.ACTION)
diff --git a/utest/test_frontend.py b/utest/test_frontend.py
index 609a262..8799000 100755
--- a/utest/test_frontend.py
+++ b/utest/test_frontend.py
@@ -56,7 +56,7 @@
 
 #version 2
 
-struct of_echo_reply {
+struct of_echo_reply(align=8) {
     uint8_t version;
     uint8_t type == 3;
     uint16_t length;
@@ -64,7 +64,7 @@
     of_octets_t data;
 };
 
-enum ofp_queue_op_failed_code {
+enum ofp_queue_op_failed_code(wire_type=uint32, bitmask=False, complete=True) {
     OFPQOFC_BAD_PORT = 0,
     OFPQOFC_BAD_QUEUE = 1,
     OFPQOFC_EPERM = 2,
@@ -81,26 +81,27 @@
         # Not testing the parser, just making sure the AST is what we expect
         expected_ast = [
             ['metadata', 'version', '1'],
-            ['enum', 'ofp_port_config', [
-                ['OFPPC_PORT_DOWN', 1],
-                ['OFPPC_NO_STP', 2],
-                ['OFPPC_NO_RECV', 4],
-                ['OFPPC_NO_RECV_STP', 8],
-                ['OFPPC_NO_FLOOD', 16],
-                ['OFPPC_NO_FWD', 32],
-                ['OFPPC_NO_PACKET_IN', 64]]],
+            ['enum', 'ofp_port_config', [], [
+                ['OFPPC_PORT_DOWN', [], 1],
+                ['OFPPC_NO_STP', [], 2],
+                ['OFPPC_NO_RECV', [], 4],
+                ['OFPPC_NO_RECV_STP', [], 8],
+                ['OFPPC_NO_FLOOD', [], 16],
+                ['OFPPC_NO_FWD', [], 32],
+                ['OFPPC_NO_PACKET_IN', [], 64]]],
             ['metadata', 'version', '2'],
-            ['struct', 'of_echo_reply', None, [
+            ['struct', 'of_echo_reply', [['align', '8']], None, [
                 ['data', 'uint8_t', 'version'],
                 ['type', 'uint8_t', 'type', 3],
                 ['data', 'uint16_t', 'length'],
                 ['data', 'uint32_t', 'xid'],
                 ['data', 'of_octets_t', 'data']]],
-            ['enum', 'ofp_queue_op_failed_code', [
-                ['OFPQOFC_BAD_PORT', 0],
-                ['OFPQOFC_BAD_QUEUE', 1],
-                ['OFPQOFC_EPERM', 2]]],
-            ['struct', 'of_packet_queue', None, [
+            ['enum', 'ofp_queue_op_failed_code',
+                [['wire_type', 'uint32'], ['bitmask','False'], ['complete', 'True']], [
+                ['OFPQOFC_BAD_PORT', [], 0],
+                ['OFPQOFC_BAD_QUEUE', [], 1],
+                ['OFPQOFC_EPERM', [], 2]]],
+            ['struct', 'of_packet_queue', [], None, [
                 ['data', 'uint32_t', 'queue_id'],
                 ['data', 'uint16_t', 'len'],
                 ['pad', 2],
@@ -111,32 +112,34 @@
         ofinput = frontend.create_ofinput(ast)
         self.assertEquals(set([1, 2]), ofinput.wire_versions)
         expected_classes = [
-            OFClass('of_echo_reply', None, [
+            OFClass(name='of_echo_reply', superclass=None, members=[
                 OFDataMember('version', 'uint8_t'), # XXX
                 OFTypeMember('type', 'uint8_t', 3),
                 OFLengthMember('length', 'uint16_t'),
                 OFDataMember('xid', 'uint32_t'),
-                OFDataMember('data', 'of_octets_t')]),
-            OFClass('of_packet_queue', None, [
+                OFDataMember('data', 'of_octets_t')], virtual=False,
+                params={'align': '8'}),
+            OFClass(name='of_packet_queue', superclass=None, members=[
                 OFDataMember('queue_id', 'uint32_t'),
                 OFLengthMember('len', 'uint16_t'),
                 OFPadMember(2),
-                OFDataMember('properties', 'list(of_queue_prop_t)')]),
+                OFDataMember('properties', 'list(of_queue_prop_t)')], virtual=False, params={}),
         ]
         self.assertEquals(expected_classes, ofinput.classes)
         expected_enums = [
-            OFEnum('ofp_port_config', [
-                ('OFPPC_PORT_DOWN', 1),
-                ('OFPPC_NO_STP', 2),
-                ('OFPPC_NO_RECV', 4),
-                ('OFPPC_NO_RECV_STP', 8),
-                ('OFPPC_NO_FLOOD', 16),
-                ('OFPPC_NO_FWD', 32),
-                ('OFPPC_NO_PACKET_IN', 64)]),
-            OFEnum('ofp_queue_op_failed_code', [
-                ('OFPQOFC_BAD_PORT', 0),
-                ('OFPQOFC_BAD_QUEUE', 1),
-                ('OFPQOFC_EPERM', 2)]),
+            OFEnum(name='ofp_port_config', entries=[
+                OFEnumEntry('OFPPC_PORT_DOWN', 1, {}),
+                OFEnumEntry('OFPPC_NO_STP', 2, {}),
+                OFEnumEntry('OFPPC_NO_RECV', 4, {}),
+                OFEnumEntry('OFPPC_NO_RECV_STP', 8, {}),
+                OFEnumEntry('OFPPC_NO_FLOOD', 16, {}),
+                OFEnumEntry('OFPPC_NO_FWD', 32, {}),
+                OFEnumEntry('OFPPC_NO_PACKET_IN', 64, {})], params={}),
+            OFEnum(name='ofp_queue_op_failed_code', entries=[
+                OFEnumEntry('OFPQOFC_BAD_PORT', 0, {}),
+                OFEnumEntry('OFPQOFC_BAD_QUEUE', 1, {}),
+                OFEnumEntry('OFPQOFC_EPERM', 2, {})],
+                params={'wire_type': 'uint32', 'bitmask': 'False', 'complete': 'True'}),
         ]
         self.assertEquals(expected_enums, ofinput.enums)
 
@@ -145,7 +148,7 @@
 #version 1
 
 struct of_queue_prop {
-    uint16_t type;
+    uint16_t type == ?;
     uint16_t len;
     pad(4);
 };
@@ -163,12 +166,12 @@
         expected_ast = [
             ['metadata', 'version', '1'],
 
-            ['struct', 'of_queue_prop', None, [
-                ['data', 'uint16_t', 'type'],
+            ['struct', 'of_queue_prop', [], None, [
+                ['discriminator', 'uint16_t', 'type'],
                 ['data', 'uint16_t', 'len'],
                 ['pad', 4]]],
 
-            ['struct', 'of_queue_prop_min_rate', 'of_queue_prop', [
+            ['struct', 'of_queue_prop_min_rate', [], 'of_queue_prop', [
                 ['type', 'uint16_t', 'type', 1],
                 ['data', 'uint16_t', 'len'],
                 ['pad', 4],
@@ -179,16 +182,16 @@
 
         ofinput = frontend.create_ofinput(ast)
         expected_classes = [
-            OFClass('of_queue_prop', None, [
-                OFDataMember('type', 'uint16_t'),
+            OFClass(name='of_queue_prop', superclass=None, members=[
+                OFDiscriminatorMember('type', 'uint16_t'),
                 OFLengthMember('len', 'uint16_t'),
-                OFPadMember(4)]),
-            OFClass('of_queue_prop_min_rate', 'of_queue_prop', [
+                OFPadMember(4)], virtual=True, params={}),
+            OFClass(name='of_queue_prop_min_rate', superclass='of_queue_prop', members= [
                 OFTypeMember('type', 'uint16_t', 1),
                 OFLengthMember('len', 'uint16_t'),
                 OFPadMember(4),
                 OFDataMember('rate', 'uint16_t'),
-                OFPadMember(6)]),
+                OFPadMember(6)], virtual=False, params= {}),
         ]
         self.assertEquals(expected_classes, ofinput.classes)
 
diff --git a/utest/test_parser.py b/utest/test_parser.py
index c24665c..9eb3764 100755
--- a/utest/test_parser.py
+++ b/utest/test_parser.py
@@ -42,7 +42,7 @@
 struct foo { };
 """
         ast = parser.parse(src)
-        self.assertEquals(ast, [['struct', 'foo', None, []]])
+        self.assertEquals(ast, [['struct', 'foo', [], None, []]])
 
     def test_one_field(self):
         src = """\
@@ -52,7 +52,17 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', None, [['data', 'uint32_t', 'bar']]]])
+            [['struct', 'foo', [], None, [['data', 'uint32_t', 'bar']]]])
+
+    def test_struct_align_arg(self):
+        src = """\
+struct foo(align=8) {
+    uint32_t bar;
+};
+"""
+        ast = parser.parse(src)
+        self.assertEquals(ast,
+            [['struct', 'foo', [['align', '8']], None, [['data', 'uint32_t', 'bar']]]])
 
     def test_multiple_fields(self):
         src = """\
@@ -64,7 +74,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', None,
+            [['struct', 'foo', [], None,
                 [['data', 'uint32_t', 'bar'],
                  ['data', 'uint8_t', 'baz'],
                  ['data', 'uint64_t', 'abc']]]])
@@ -77,7 +87,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', None, [['data', 'uint32_t[4]', 'bar']]]])
+            [['struct', 'foo', [], None, [['data', 'uint32_t[4]', 'bar']]]])
 
     def test_list_type(self):
         src = """\
@@ -87,7 +97,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', None, [['data', 'list(of_action_t)', 'bar']]]])
+            [['struct', 'foo', [], None, [['data', 'list(of_action_t)', 'bar']]]])
 
     def test_pad_member(self):
         src = """\
@@ -97,7 +107,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', None, [['pad', 1]]]])
+            [['struct', 'foo', [], None, [['pad', 1]]]])
 
     def test_type_member(self):
         src = """\
@@ -107,7 +117,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', None, [['type', 'uint16_t', 'foo', 0x10]]]])
+            [['struct', 'foo', [], None, [['type', 'uint16_t', 'foo', 0x10]]]])
 
     def test_inheritance(self):
         src = """\
@@ -117,7 +127,17 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', 'bar', [['type', 'uint16_t', 'foo', 0x10]]]])
+            [['struct', 'foo', [], 'bar', [['type', 'uint16_t', 'foo', 0x10]]]])
+
+    def test_discriminator(self):
+        src = """\
+struct foo {
+    uint16_t foo == ?;
+};
+"""
+        ast = parser.parse(src)
+        self.assertEquals(ast,
+            [['struct', 'foo', [], None, [['discriminator', 'uint16_t', 'foo']]]])
 
 class EnumTests(unittest.TestCase):
     def test_empty(self):
@@ -126,7 +146,7 @@
 };
 """
         ast = parser.parse(src)
-        self.assertEquals(ast, [['enum', 'foo', []]])
+        self.assertEquals(ast, [['enum', 'foo', [], []]])
 
     def test_one(self):
         src = """\
@@ -135,7 +155,18 @@
 };
 """
         ast = parser.parse(src)
-        self.assertEquals(ast, [['enum', 'foo', [['BAR', 1]]]])
+        self.assertEquals(ast, [['enum', 'foo', [], [['BAR', [], 1]]]])
+
+    def test_params(self):
+        src = """\
+enum foo(wire_type=uint32, bitmask=False, complete=False) {
+    BAR = 1
+};
+"""
+        ast = parser.parse(src)
+        self.assertEquals(ast, [['enum', 'foo',
+            [ ['wire_type', 'uint32'], ['bitmask','False'], ['complete', 'False']],
+            [['BAR', [], 1]]]])
 
     def test_multiple(self):
         src = """\
@@ -146,7 +177,7 @@
 };
 """
         ast = parser.parse(src)
-        self.assertEquals(ast, [['enum', 'foo', [['OFP_A', 1], ['OFP_B', 2], ['OFP_C', 3]]]])
+        self.assertEquals(ast, [['enum', 'foo', [], [['OFP_A', [], 1], ['OFP_B', [], 2], ['OFP_C', [], 3]]]])
 
     def test_trailing_comma(self):
         src = """\
@@ -157,7 +188,7 @@
 };
 """
         ast = parser.parse(src)
-        self.assertEquals(ast, [['enum', 'foo', [['OFP_A', 1], ['OFP_B', 2], ['OFP_C', 3]]]])
+        self.assertEquals(ast, [['enum', 'foo', [], [['OFP_A', [], 1], ['OFP_B', [], 2], ['OFP_C', [], 3]]]])
 
 class TestMetadata(unittest.TestCase):
     def test_version(self):
@@ -175,7 +206,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', None, []], ['struct', 'bar', None, []]])
+            [['struct', 'foo', [], None, []], ['struct', 'bar', [], None, []]])
 
     def test_comments(self):
         src = """\
@@ -189,7 +220,7 @@
 """
         ast = parser.parse(src)
         self.assertEquals(ast,
-            [['struct', 'foo', None, [['data', 'uint32_t', 'a']]]])
+            [['struct', 'foo', [], None, [['data', 'uint32_t', 'a']]]])
 
     def test_mixed(self):
         src = """\
@@ -201,9 +232,9 @@
         ast = parser.parse(src)
         self.assertEquals(ast,
             [['metadata', 'version', '1'],
-             ['struct', 'foo', None, []],
+             ['struct', 'foo', [], None, []],
              ['metadata', 'version', '2'],
-             ['struct', 'bar', None, []]])
+             ['struct', 'bar', [], None, []]])
 
 class TestErrors(unittest.TestCase):
     def syntax_error(self, src, regex):