Merge into master from pull request #151:
automatically generate action/instruction ID classes (https://github.com/floodlight/loxigen/pull/151)
diff --git a/c_gen/build_of_g.py b/c_gen/build_of_g.py
index 2a272ce..f117bfd 100755
--- a/c_gen/build_of_g.py
+++ b/c_gen/build_of_g.py
@@ -443,28 +443,6 @@
                 of_g.ordered_classes[wire_version].append(new_cls)
                 classes[new_cls] = classes[cls]
 
-    # Generate action_id classes for OF 1.3
-    for wire_version, ordered_classes in of_g.ordered_classes.items():
-        if not wire_version in [of_g.VERSION_1_3]:
-            continue
-        classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
-        for cls in ordered_classes:
-            if not loxi_utils.class_is_action(cls):
-                continue
-            action = cls[10:]
-            if action == '' or action == 'header':
-                continue
-            name = "of_action_id_" + action
-            members = classes["of_action"][:]
-            of_g.ordered_classes[wire_version].append(name)
-            if type_maps.action_id_is_extension(name, wire_version):
-                # Copy the base action classes thru subtype
-                members = classes["of_action_" + action][:4]
-            classes[name] = members
-
-    # @fixme If we support extended actions in OF 1.3, need to add IDs
-    # for them here
-
     for wire_version in of_g.wire_ver_map.keys():
         version_name = of_g.of_version_wire2name[wire_version]
         calculate_offsets_and_lengths(
diff --git a/c_gen/loxi_utils_legacy.py b/c_gen/loxi_utils_legacy.py
index e20155d..9abca52 100644
--- a/c_gen/loxi_utils_legacy.py
+++ b/c_gen/loxi_utils_legacy.py
@@ -180,6 +180,8 @@
     """
     Return True if cls_name is an instruction object
     """
+    if cls.find("of_instruction_id") == 0:
+        return False
     if cls.find("of_instruction") == 0:
         return True
 
diff --git a/c_gen/type_maps.py b/c_gen/type_maps.py
index ae5f481..e709b70 100644
--- a/c_gen/type_maps.py
+++ b/c_gen/type_maps.py
@@ -60,21 +60,25 @@
     of_g.VERSION_1_3:dict()
     }
 
-# HACK shared between actions and action_ids
-of_1_3_action_types = dict()
+instruction_id_types = {
+    of_g.VERSION_1_0:dict(),
+    of_g.VERSION_1_1:dict(),
+    of_g.VERSION_1_2:dict(),
+    of_g.VERSION_1_3:dict()
+    }
 
 action_types = {
     of_g.VERSION_1_0:dict(),
     of_g.VERSION_1_1:dict(),
     of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:of_1_3_action_types
+    of_g.VERSION_1_3:dict(),
     }
 
 action_id_types = {
     of_g.VERSION_1_0:dict(),
     of_g.VERSION_1_1:dict(),
     of_g.VERSION_1_2:dict(),
-    of_g.VERSION_1_3:of_1_3_action_types
+    of_g.VERSION_1_3:dict(),
     }
 
 queue_prop_types = {
@@ -134,6 +138,7 @@
 # All inheritance data for non-messages
 inheritance_data = dict(
     of_instruction = instruction_types,
+    of_instruction_id = instruction_id_types,
     of_action = action_types,
     of_action_id = action_id_types,
     of_oxm = oxm_types,
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index 8f5434e..7aa4857 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -470,6 +470,15 @@
                 return ("action", "OFActionExperimenter", None)
             else:
                 return ("action", "OFAction", None)
+        elif self.ir_class.is_instanceof("of_action_id"):
+            if self.ir_class.is_subclassof('of_action_id_bsn'):
+                return ("actionid", "OFActionIdBsn", None)
+            elif self.ir_class.is_subclassof('of_action_id_nicira'):
+                return ("actionid", "OFActionIdNicira", None)
+            elif self.ir_class.is_subclassof('of_action_id_experimenter'):
+                return ("actionid", "OFActionIdExperimenter", None)
+            else:
+                return ("actionid", "OFActionId", None)
         elif self.ir_class.is_instruction:
             if self.ir_class.is_subclassof('of_instruction_bsn'):
                 return ("instruction", "OFInstructionBsn", None)
@@ -477,6 +486,13 @@
                 return ("instruction", "OFInstructionExperimenter", None)
             else:
                 return ("instruction", "OFInstruction", None)
+        elif self.ir_class.is_instanceof('of_instruction_id'):
+            if self.ir_class.is_subclassof('of_instruction_id_bsn'):
+                return ("instructionid", "OFInstructionIdBsn", None)
+            elif self.ir_class.is_subclassof('of_instruction_id_experimenter'):
+                return ("instructionid", "OFInstructionIdExperimenter", None)
+            else:
+                return ("instructionid", "OFInstructionId", None)
         elif re.match(r'OFBsnVport.+$', self.name):
             return ("", "OFBsnVport", None)
         elif self.name == "OFOxm":
diff --git a/java_gen/templates/_imports.java b/java_gen/templates/_imports.java
index cf7334d..af529bc 100644
--- a/java_gen/templates/_imports.java
+++ b/java_gen/templates/_imports.java
@@ -7,8 +7,10 @@
 import java.util.Map;
 import org.projectfloodlight.openflow.protocol.*;
 import org.projectfloodlight.openflow.protocol.action.*;
+import org.projectfloodlight.openflow.protocol.actionid.*;
 import org.projectfloodlight.openflow.protocol.meterband.*;
 import org.projectfloodlight.openflow.protocol.instruction.*;
+import org.projectfloodlight.openflow.protocol.instructionid.*;
 import org.projectfloodlight.openflow.protocol.match.*;
 import org.projectfloodlight.openflow.protocol.oxm.*;
 import org.projectfloodlight.openflow.protocol.queueprop.*;
diff --git a/lang_python.py b/lang_python.py
index f40cecc..9087b43 100644
--- a/lang_python.py
+++ b/lang_python.py
@@ -50,6 +50,8 @@
             of12: ...           # (code generation incomplete)
                 oxm.py          # OXM classes
             of13: ...           # (code generation incomplete)
+                action_id.py    # Action ID classes
+                instruction_id.py # Instruction ID classes
                 meter_band.py   # Meter band classes
 
 The user will add the pyloxi directory to PYTHONPATH. Then they can
@@ -83,7 +85,7 @@
     1: ["action", "common", "const", "message", "util"],
     2: ["action", "common", "const", "instruction", "message", "util"],
     3: ["action", "common", "const", "instruction", "message", "oxm", "util"],
-    4: ["action", "common", "const", "instruction", "message", "meter_band", "oxm", "util"],
+    4: ["action", "action_id", "common", "const", "instruction", "instruction_id", "message", "meter_band", "oxm", "util"],
 }
 
 def make_gen(name, version):
diff --git a/loxi_ir/ir.py b/loxi_ir/ir.py
index df1c77d..ebd4f85 100644
--- a/loxi_ir/ir.py
+++ b/loxi_ir/ir.py
@@ -419,9 +419,52 @@
         build_touch_classes.remove(name)
         return c
 
+    def build_id_class(orig_name, base_name):
+        name = base_name + '_id' + orig_name[len(base_name):]
+        if name in name_classes:
+            return name_classes[name]
+        orig_fe, _ = name_frontend_classes[orig_name]
+
+        if orig_fe.superclass:
+            superclass_name = base_name + '_id' + orig_fe.superclass[len(base_name):]
+            superclass = build_id_class(orig_fe.superclass, base_name)
+        else:
+            superclass_name = None
+            superclass = None
+
+        fe = frontend_ir.OFClass(
+            name=name,
+            superclass=superclass_name,
+            members=[m for m in orig_fe.members if not isinstance(m, frontend_ir.OFDataMember)],
+            virtual=orig_fe.virtual,
+            params={})
+
+        base_length, is_fixed_length, member_lengths = \
+           ir_offset.calc_lengths(version, fe, name_classes, name_enums)
+        assert fe.virtual or is_fixed_length
+
+        members = []
+        c = OFClass(name=fe.name, superclass=superclass,
+                members=members, virtual=fe.virtual, params=fe.params,
+                is_fixed_length=is_fixed_length, base_length=base_length)
+
+        members.extend( build_member(c, fe_member, member_lengths[fe_member])
+                  for fe_member in fe.members)
+
+        name_classes[name] = c
+        return c
+
+    id_class_roots = ["of_action", "of_instruction"]
+
     for name in sorted(name_frontend_classes.keys()):
         c = build_class(name)
 
+        # Build ID classes for OF 1.3+
+        if version.wire_version >= 4:
+            for root in id_class_roots:
+                if c.is_instanceof(root):
+                    build_id_class(name, root)
+
     protocol = OFProtocol(version=version, classes=tuple(name_classes.values()), enums=tuple(name_enums.values()))
     for e in chain(protocol.classes, protocol.enums):
         e.protocol = protocol
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index 2c4a135..de3fdba 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -40,8 +40,10 @@
 roots = {
     'of_header': 'message',
     'of_action': 'action',
+    'of_action_id': 'action_id',
     'of_oxm': 'oxm',
     'of_instruction': 'instruction',
+    'of_instruction_id': 'instruction_id',
     'of_meter_band': 'meter_band',
 }
 
@@ -74,6 +76,11 @@
                          ofclasses=modules_by_version[version]['action'],
                          version=version)
 
+def generate_action_id(out, name, version):
+    util.render_template(out, 'module.py',
+                         ofclasses=modules_by_version[version]['action_id'],
+                         version=version)
+
 def generate_oxm(out, name, version):
     util.render_template(out, 'module.py',
                          ofclasses=modules_by_version[version]['oxm'],
@@ -94,6 +101,11 @@
                          ofclasses=modules_by_version[version]['instruction'],
                          version=version)
 
+def generate_instruction_id(out, name, version):
+    util.render_template(out, 'module.py',
+                         ofclasses=modules_by_version[version]['instruction_id'],
+                         version=version)
+
 def generate_message(out, name, version):
     util.render_template(out, 'module.py',
                          ofclasses=modules_by_version[version]['message'],
diff --git a/py_gen/templates/module.py b/py_gen/templates/module.py
index 252bf0d..30c45ad 100644
--- a/py_gen/templates/module.py
+++ b/py_gen/templates/module.py
@@ -43,6 +43,8 @@
 import oxm
 :: #endif
 :: if version >= OFVersions.VERSION_1_3:
+import action_id
+import instruction_id
 import meter_band
 :: #endif
 import util
diff --git a/py_gen/templates/util.py b/py_gen/templates/util.py
index 8ad246a..ed2698a 100644
--- a/py_gen/templates/util.py
+++ b/py_gen/templates/util.py
@@ -41,6 +41,8 @@
 import oxm
 :: #endif
 :: if version >= OFVersions.VERSION_1_3:
+import action_id
+import instruction_id
 import meter_band
 :: #endif