java_gen: add documentation
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index 4ac497f..9e5731b 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -114,6 +114,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 +192,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 +213,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 +256,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 +276,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 +310,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 +345,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):
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 38ce1ff..78ae9d6 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:
@@ -59,8 +66,13 @@
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 """
+ """ 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, size=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:
@@ -76,15 +88,31 @@
# 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)
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 +130,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 +158,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 +172,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,17 +180,28 @@
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("[]")
+##### Predefined JType mappings
+# FIXME: This list needs to be pruned / cleaned up. Most of these are schematic.
+
u8 = JType('byte', size=1) \
.op(read='bb.readByte()', write='bb.writeByte($name)')
u8_list = JType('List<U8>', size=1) \
@@ -265,7 +323,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,
@@ -277,26 +336,26 @@
}
-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"""
@@ -320,3 +379,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]