camera_metadata: add typedefs for java generated code

Change-Id: I619261b9645cda669a3b5ee7c79f4c56d4d4c3d4
diff --git a/camera/docs/metadata_model.py b/camera/docs/metadata_model.py
index 6b70288..66fe9d6 100644
--- a/camera/docs/metadata_model.py
+++ b/camera/docs/metadata_model.py
@@ -32,6 +32,7 @@
   EnumValue: A class corresponding to a <value> element within an Enum
   Metadata: Root node that also provides tree construction functionality.
   Tag: A node corresponding to a top level <tag> element.
+  Typedef: A node corresponding to a <typedef> element under <types>.
 """
 
 import sys
@@ -195,6 +196,7 @@
     parent: An edge to the parent Node. This is always None for Metadata.
     outer_namespaces: A sequence of immediate OuterNamespace children.
     tags: A sequence of all Tag instances available in the graph.
+    types: An iterable of all Typedef instances available in the graph.
   """
 
   def __init__(self):
@@ -214,6 +216,7 @@
     self._parent = None
     self._outer_namespaces = None
     self._tags = []
+    self._types = []
 
   @property
   def outer_namespaces(self):
@@ -226,6 +229,10 @@
   def tags(self):
     return (i for i in self._tags)
 
+  @property
+  def types(self):
+    return (i for i in self._types)
+
   def _get_properties(self):
 
     for i in self._entries:
@@ -253,6 +260,33 @@
     if not tag_ids:
       self._tags.append(Tag(tag, self, description))
 
+  def insert_type(self, type_name, type_selector="typedef", **kwargs):
+    """
+    Insert a type into the metadata.
+
+    Args:
+      type_name: A type's name
+      type_selector: The selector for the type, e.g. 'typedef'
+
+    Args (if type_selector == 'typedef'):
+      languages: A map of 'language name' -> 'fully qualified class path'
+
+    Example:
+      metadata.insert_type('rectangle', 'typedef',
+                           { 'java': 'android.graphics.Rect' })
+
+    Remarks:
+      Subsequent calls to insert_type with the same type name are safe (they
+      will be ignored)
+    """
+
+    if type_selector != 'typedef':
+      raise ValueError("Unsupported type_selector given " + type_selector)
+
+    type_names = [tp.name for tp in self.types if tp.name == tp]
+    if not type_names:
+      self._types.append(Typedef(type_name, self, kwargs.get('languages')))
+
   def insert_entry(self, entry):
     """
     Insert an entry into the metadata.
@@ -332,6 +366,8 @@
     self.validate_tree()
     self._construct_tags()
     self.validate_tree()
+    self._construct_types()
+    self.validate_tree()
     self._construct_clones()
     self.validate_tree()
     self._construct_outer_namespaces()
@@ -350,6 +386,16 @@
         if p not in tag.entries:
           tag._entries.append(p)
 
+  def _construct_types(self):
+    type_dict = self._dictionary_by_name(self.types)
+    for p in self._get_properties():
+      if p._type_name:
+        type_node = type_dict.get(p._type_name)
+        p._typedef = type_node
+
+        if p not in type_node.entries:
+          type_node._entries.append(p)
+
   def _construct_clones(self):
     for p in self._clones:
       target_kind = p.target_kind
@@ -567,6 +613,36 @@
   def _get_children(self):
     return None
 
+class Typedef(Node):
+  """
+  A typedef Node corresponding to a <typedef> element under a top-level <types>.
+
+  Attributes (Read-Only):
+    name: The name of this typedef as a string.
+    languages: A dictionary of 'language name' -> 'fully qualified class'.
+    parent: An edge to the parent, which is always the Metadata root node.
+    entries: An iterable over all entries which reference this typedef.
+  """
+  def __init__(self, name, parent, languages=None):
+    self._name        = name
+    self._parent      = parent
+
+    # all entries that have this typedef
+    self._entries     = []  # filled in by Metadata#construct_types
+
+    self._languages   = languages or {}
+
+  @property
+  def languages(self):
+    return self._languages
+
+  @property
+  def entries(self):
+    return (i for i in self._entries)
+
+  def _get_children(self):
+    return None
+
 class OuterNamespace(Node):
   """
   A node corresponding to a <namespace> element under <metadata>
@@ -949,6 +1025,7 @@
     units: A string units, or None.
     tags: A sequence of Tag nodes associated with this Entry.
     type_notes: A string describing notes for the type, or None.
+    typedef: A Typedef associated with this Entry, or None.
 
   Remarks:
     Subclass Clone can be used interchangeable with an Entry,
@@ -989,6 +1066,7 @@
       type_notes: A string with the notes for the type
       visibility: A string describing the visibility, eg 'system', 'hidden',
                   'public'
+      typedef: A string corresponding to a typedef's name attribute.
     """
 
     if kwargs.get('type') is None:
@@ -1070,6 +1148,10 @@
     return self._type_notes
 
   @property
+  def typedef(self):
+    return self._typedef
+
+  @property
   def enum(self):
     return self._enum
 
@@ -1091,7 +1173,7 @@
 
   def _init_common(self, **kwargs):
 
-    self._parent = None # filled in by MetadataSet::_construct_entries
+    self._parent = None # filled in by Metadata::_construct_entries
 
     self._container = kwargs.get('container')
     self._container_sizes = kwargs.get('container_sizes')
@@ -1109,9 +1191,11 @@
     self._notes = kwargs.get('notes')
 
     self._tag_ids = kwargs.get('tag_ids', [])
-    self._tags = None  # Filled in by MetadataSet::_construct_tags
+    self._tags = None  # Filled in by Metadata::_construct_tags
 
     self._type_notes = kwargs.get('type_notes')
+    self._type_name = kwargs.get('type_name')
+    self._typedef = None # Filled in by Metadata::_construct_types
 
     if kwargs.get('enum', False):
       self._enum = Enum(self, enum_values, enum_ids, enum_optionals, enum_notes)
@@ -1312,7 +1396,8 @@
                     'tuple_values',
                     'type',
                     'type_notes',
-                    'visibility'
+                    'visibility',
+                    'typedef'
                    ]
 
     for p in props_common: