camera_metadata: Generate java metadata keys source code

Change-Id: Id1d1d4367eb51354e85c4eea38c593a498932e5b
diff --git a/camera/docs/metadata_model.py b/camera/docs/metadata_model.py
index fa85a58..daf171c 100644
--- a/camera/docs/metadata_model.py
+++ b/camera/docs/metadata_model.py
@@ -23,12 +23,13 @@
   Node: Base class for most nodes.
   Entry: A node corresponding to <entry> elements.
   Clone: A node corresponding to <clone> elements.
+  MergedEntry: A node corresponding to either <entry> or <clone> elements.
   Kind: A node corresponding to <dynamic>, <static>, <controls> elements.
   InnerNamespace: A node corresponding to a <namespace> nested under a <kind>.
   OuterNamespace: A node corresponding to a <namespace> with <kind> children.
   Section: A node corresponding to a <section> element.
   Enum: A class corresponding an <enum> element within an <entry>
-  Value: A class corresponding to a <value> element within an Enum
+  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.
 """
@@ -80,7 +81,6 @@
       for j in i.find_all(pred):
         yield j
 
-
   def find_first(self, pred):
     """
     Find the first descendant that matches the predicate.
@@ -147,7 +147,7 @@
 
   def _children_name_map_matching(self, match=lambda x: True):
     d = {}
-    for i in _get_children():
+    for i in self._get_children():
       if match(i):
         d[i.name] = i
     return d
@@ -283,7 +283,6 @@
       (they will be ignored). Also the target entry need not be inserted
       ahead of the clone entry.
     """
-    entry_name = clone['name']
     # figure out corresponding entry later. allow clone insert, entry insert
     entry = None
     c = Clone(entry, **clone)
@@ -310,7 +309,7 @@
       if p.parent is not None:
         p.parent._entries.remove(p)
       # remove from parents' _leafs list
-      for ancestor in p.find_parents(lambda x: not isinstance(x, MetadataSet)):
+      for ancestor in p.find_parents(lambda x: not isinstance(x, Metadata)):
         ancestor._leafs.remove(p)
 
       # remove from global list
@@ -652,6 +651,37 @@
     for k in new_kinds_lst:
       yield k
 
+  def combine_kinds_into_single_node(self):
+    r"""
+    Combines the section's Kinds into a single node.
+
+    Combines all the children (kinds) of this section into a single
+    virtual Kind node.
+
+    Returns:
+      A new Kind node that collapses all Kind siblings into one, combining
+      all their children together.
+
+      For example, given self.kinds == [ x, y ]
+
+        x  y               z
+      / |  | \    -->   / | | \
+      a b  c d          a b c d
+
+      a new instance z is returned in this example.
+
+    Remarks:
+      The children of the kinds are the same references as before, that is
+      their parents will point to the old parents and not to the new parent.
+    """
+    combined = Kind(name="combined", parent=self)
+
+    for k in self._get_children():
+      combined._namespaces.extend(k.namespaces)
+      combined._entries.extend(k.entries)
+
+    return combined
+
 class Kind(Node):
   """
   A node corresponding to one of: <static>,<dynamic>,<controls> under a
@@ -695,6 +725,63 @@
     for i in self.entries:
       yield i
 
+  def combine_children_by_name(self):
+    r"""
+    Combine multiple children with the same name into a single node.
+
+    Returns:
+      A new Kind where all of the children with the same name were combined.
+
+      For example:
+
+      Given a Kind k:
+
+              k
+            / | \
+            a b c
+            | | |
+            d e f
+
+      a.name == "foo"
+      b.name == "foo"
+      c.name == "bar"
+
+      The returned Kind will look like this:
+
+             k'
+            /  \
+            a' c'
+          / |  |
+          d e  f
+
+    Remarks:
+      This operation is not recursive. To combine the grandchildren and other
+      ancestors, call this method on the ancestor nodes.
+    """
+    return Kind._combine_children_by_name(self, new_type=type(self))
+
+  # new_type is either Kind or InnerNamespace
+  @staticmethod
+  def _combine_children_by_name(self, new_type):
+    new_ins_dict = OrderedDict()
+    new_ent_dict = OrderedDict()
+
+    for ins in self.namespaces:
+      new_ins = new_ins_dict.setdefault(ins.name,
+                                        InnerNamespace(ins.name, parent=self))
+      new_ins._namespaces.extend(ins.namespaces)
+      new_ins._entries.extend(ins.entries)
+
+    for ent in self.entries:
+      new_ent = new_ent_dict.setdefault(ent.name,
+                                        ent.merge())
+
+    kind = new_type(self.name, self.parent)
+    kind._namespaces = new_ins_dict.values()
+    kind._entries = new_ent_dict.values()
+
+    return kind
+
 class InnerNamespace(Node):
   """
   A node corresponding to a <namespace> which is an ancestor of a Kind.
@@ -737,6 +824,42 @@
     for i in self.entries:
       yield i
 
+  def combine_children_by_name(self):
+    r"""
+    Combine multiple children with the same name into a single node.
+
+    Returns:
+      A new InnerNamespace where all of the children with the same name were
+      combined.
+
+      For example:
+
+      Given an InnerNamespace i:
+
+              i
+            / | \
+            a b c
+            | | |
+            d e f
+
+      a.name == "foo"
+      b.name == "foo"
+      c.name == "bar"
+
+      The returned InnerNamespace will look like this:
+
+             i'
+            /  \
+            a' c'
+          / |  |
+          d e  f
+
+    Remarks:
+      This operation is not recursive. To combine the grandchildren and other
+      ancestors, call this method on the ancestor nodes.
+    """
+    return Kind._combine_children_by_name(self, new_type=type(self))
+
 class EnumValue(Node):
   """
   A class corresponding to a <value> element within an <enum> within an <entry>.
@@ -777,6 +900,8 @@
   Attributes (Read-Only):
     parent: An edge to the parent, always an Entry instance.
     values: A sequence of EnumValue children.
+    has_values_with_id: A boolean representing if any of the children have a
+        non-empty id property.
   """
   def __init__(self, parent, values, ids={}, optionals=[], notes={}):
     self._values =                                                             \
@@ -790,6 +915,10 @@
   def values(self):
     return (i for i in self._values)
 
+  @property
+  def has_values_with_id(self):
+    return bool(any(i for i in self.values if i.id))
+
   def _get_children(self):
     return (i for i in self._values)
 
@@ -954,8 +1083,8 @@
     # access these via the 'enum' prop
     enum_values = kwargs.get('enum_values')
     enum_optionals = kwargs.get('enum_optionals')
-    enum_notes = kwargs.get('enum_notes') # { value => notes }
-    enum_ids = kwargs.get('enum_ids') # { value => notes }
+    enum_notes = kwargs.get('enum_notes')  # { value => notes }
+    enum_ids = kwargs.get('enum_ids')  # { value => notes }
     self._tuple_values = kwargs.get('tuple_values')
 
     self._description = kwargs.get('description')
@@ -964,7 +1093,7 @@
     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 MetadataSet::_construct_tags
 
     self._type_notes = kwargs.get('type_notes')
 
@@ -1106,9 +1235,9 @@
       Note that type is not specified since it has to be the same as the
       entry.type.
     """
-    self._entry = entry # Entry object
+    self._entry = entry  # Entry object
     self._target_kind = kwargs['target_kind']
-    self._name = kwargs['name'] # same as entry.name
+    self._name = kwargs['name']  # same as entry.name
     self._kind = kwargs['kind']
 
     # illegal to override the type, it should be the same as the entry