loxigen: generate legacy type_maps data from the IR

The C backend (and, for the moment, the python and java backends) still need
the type_maps data. Duplicating it between the type_maps file and the input
files is not ideal because adding a new object would require editing both
places.

This change does the minimal work to generate most of the type_maps data from
the IR. There are some stragglers where LOXI is particularly unclean (stats and
flow-mod subtypes not really using inheritance) but the goal is for a developer
who needs to add a new object to be able to do it without modifying Python
code.
diff --git a/loxigen.py b/loxigen.py
index 5787791..c6c344d 100755
--- a/loxigen.py
+++ b/loxigen.py
@@ -457,6 +457,75 @@
             ofprotocol.classes.sort(key=lambda ofclass: ofclass.name)
             of_g.ir[wire_version] = ofprotocol
 
+def populate_type_maps():
+    """
+    Use the type members in the IR to fill out the legacy type_maps.
+    """
+
+    def split_inherited_cls(cls):
+        if cls == 'of_meter_band_stats': # HACK not a subtype of of_meter_band
+            return None, None
+        for parent in sorted(type_maps.inheritance_data.keys(), reverse=True):
+            if cls.startswith(parent):
+                return (parent, cls[len(parent)+1:])
+        return None, None
+
+    def find_experimenter(parent, cls):
+        for experimenter in sorted(of_g.experimenter_name_to_id.keys(), reverse=True):
+            prefix = parent + '_' + experimenter
+            if cls.startswith(prefix):
+                return experimenter
+        return None
+
+    def find_type_value(ofclass, m_name):
+        for m in ofclass.members:
+            if isinstance(m, OFTypeMember) and m.name == m_name:
+                return m.value
+        raise KeyError("ver=%d, cls=%s, m_name=%s" % (wire_version, cls, m_name))
+
+    # Most inheritance classes: actions, instructions, etc
+    for wire_version, protocol in of_g.ir.items():
+        for ofclass in protocol.classes:
+            cls = ofclass.name
+            parent, subcls = split_inherited_cls(cls)
+            if not (parent and subcls):
+                continue
+            if parent == 'of_oxm':
+                val = (find_type_value(ofclass, 'type_len') >> 8) & 0xff
+            else:
+                val = find_type_value(ofclass, 'type')
+            type_maps.inheritance_data[parent][wire_version][subcls] = val
+
+            # Extensions (only actions for now)
+            experimenter = find_experimenter(parent, cls)
+            if parent == 'of_action' and experimenter:
+                val = find_type_value(ofclass, 'subtype')
+                type_maps.extension_action_subtype[wire_version][experimenter][cls] = val
+                if wire_version >= of_g.VERSION_1_3:
+                    cls2 = parent + "_id" + cls[len(parent):]
+                    type_maps.extension_action_id_subtype[wire_version][experimenter][cls2] = val
+
+    # Messages
+    for wire_version, protocol in of_g.ir.items():
+        for ofclass in protocol.classes:
+            cls = ofclass.name
+            # HACK (though this is what loxi_utils.class_is_message() does)
+            if not [x for x in ofclass.members if isinstance(x, OFDataMember) and x.name == 'xid']:
+                continue
+            if cls == 'of_header':
+                continue
+            subcls = cls[3:]
+            val = find_type_value(ofclass, 'type')
+            type_maps.message_types[wire_version][subcls] = val
+
+            # Extensions
+            experimenter = find_experimenter('of', cls)
+            if experimenter:
+                val = find_type_value(ofclass, 'subtype')
+                type_maps.extension_message_subtype[wire_version][experimenter][cls] = val
+
+    type_maps.generate_maps()
+
 def analyze_input():
     """
     Add information computed from the input, including offsets and
@@ -577,8 +646,8 @@
     log("\nGenerating files for target language %s\n" % of_g.options.lang)
 
     initialize_versions()
-    type_maps.generate_maps()
     read_input()
+    populate_type_maps()
     analyze_input()
     unify_input()
     order_and_assign_object_ids()