Camera: Docs XML - allow multiple kinds per section

- When adding a new entry and maintaining ABI, make a new kind if can't be
  semantically part of the last kind. Don't add to the end of an existing
  non-last kind.

- Print generated file names in metadata-generate

(Aside) New sections should also be ended at the end of the root namespace to
  maintain ABI compatibility.

Change-Id: I5df0a701763af3cc91ee6eead4fe9fd423c9aa86
diff --git a/camera/docs/html.mako b/camera/docs/html.mako
index 4866472..d647f90 100644
--- a/camera/docs/html.mako
+++ b/camera/docs/html.mako
@@ -137,7 +137,7 @@
       <tr class="description"><td>${section.description}</td></tr>
     % endif
 
-    % for kind in section.kinds: # dynamic,static,controls
+    % for kind in section.merged_kinds: # dynamic,static,controls
       <tr><td colspan="7" class="kind">${kind.name}</td></tr>
 
       <thead>
diff --git a/camera/docs/metadata-generate b/camera/docs/metadata-generate
index a9e53d8..38f8ac7 100755
--- a/camera/docs/metadata-generate
+++ b/camera/docs/metadata-generate
@@ -25,12 +25,26 @@
 
 thisdir=$(dirname $(readlink -f $0))
 
+function relpath() {
+    python -c "import os.path; print os.path.relpath('$1', '$PWD')"
+}
+
 function gen_file() {
     local in=$thisdir/$1
     local out=$thisdir/$2
 
     python $thisdir/metadata_parser_xml.py $thisdir/metadata_properties.xml $in > $out
-    return $?
+
+    local succ=$?
+
+    if [[ $succ -eq 0 ]]
+    then
+        echo "OK: Generated $(relpath "$out")"
+    else
+        echo "FAIL: Errors while generating $(relpath "$out")" >& 2
+    fi
+
+    return $succ
 }
 
 $thisdir/metadata-check-dependencies || exit 1
diff --git a/camera/docs/metadata_model.py b/camera/docs/metadata_model.py
index e2a2f0e..2091698 100644
--- a/camera/docs/metadata_model.py
+++ b/camera/docs/metadata_model.py
@@ -34,6 +34,7 @@
 """
 
 import sys
+import itertools
 from collections import OrderedDict
 
 class Node(object):
@@ -429,42 +430,35 @@
 
   # 'controls', 'static' 'dynamic'. etc
   def _construct_kinds(self, section):
-
-    kinds_dict = self._dictionary_by_name(section.kinds)
-    for name, kind in kinds_dict.iteritems():
+    for kind in section.kinds:
       kind._leafs = []
       section.validate_tree()
 
-    for p in section._leafs:
-      kind = kinds_dict.get(p.kind, Kind(p.kind, section))
-      kinds_dict[p.kind] = kind
-      section.validate_tree()
+    group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind)
+    leaf_it = ((k, g) for k, g in group_entry_by_kind)
 
-      if p not in kind._leafs:
-        kind._leafs.append(p)
+    # allow multiple kinds with the same name. merge if adjacent
+    # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic
+    # this helps maintain ABI compatibility when adding an entry in a new kind
+    for idx, (kind_name, entry_it) in enumerate(leaf_it):
+      if idx >= len(section._kinds):
+        kind = Kind(kind_name, section)
+        section._kinds.append(kind)
+        section.validate_tree()
 
-    if len(kinds_dict) > 3:
-      sec = section
-      if sec is not None:
-        sec_name = sec.name
-      else:
-        sec_name = "Unknown"
+      kind = section._kinds[idx]
 
-      print >> sys.stderr, ("ERROR: Kind '%s' has too many children(%d) " +    \
-                            "in section '%s'") %(name, len(kc), sec_name)
+      for p in entry_it:
+        if p not in kind._leafs:
+          kind._leafs.append(p)
 
-
-    for name, kind in kinds_dict.iteritems():
-
+    for kind in section._kinds:
       kind.validate_tree()
       self._construct_inner_namespaces(kind)
       kind.validate_tree()
       self._construct_entries(kind)
       kind.validate_tree()
 
-      if kind not in section.kinds:
-        section._kinds.append(kind)
-
       if not section.validate_tree():
         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
                              "construct_kinds, with kind = '%s'") %(kind)
@@ -604,6 +598,8 @@
     parent: An edge to the parent, which is always an OuterNamespace instance.
     description: A string description of the section, or None.
     kinds: A sequence of Kind children.
+    merged_kinds: A sequence of virtual Kind children,
+                  with each Kind's children merged by the kind.name
   """
   def __init__(self, name, parent, description=None, kinds=[]):
     self._name = name
@@ -635,6 +631,27 @@
   def _get_children(self):
     return (i for i in self.kinds)
 
+  @property
+  def merged_kinds(self):
+
+    def aggregate_by_name(acc, el):
+      existing = [i for i in acc if i.name == el.name]
+      if existing:
+        k = existing[0]
+      else:
+        k = Kind(el.name, el.parent)
+        acc.append(k)
+
+      k._namespaces.extend(el._namespaces)
+      k._entries.extend(el._entries)
+
+      return acc
+
+    new_kinds_lst = reduce(aggregate_by_name, self.kinds, [])
+
+    for k in new_kinds_lst:
+      yield k
+
 class Kind(Node):
   """
   A node corresponding to one of: <static>,<dynamic>,<controls> under a
diff --git a/camera/docs/metadata_properties.xsd b/camera/docs/metadata_properties.xsd
index 48a2c26..259aebf 100644
--- a/camera/docs/metadata_properties.xsd
+++ b/camera/docs/metadata_properties.xsd
@@ -37,11 +37,13 @@
     </complexType>
 
     <complexType name="SectionType">
-        <all>
-            <element name="controls" type="tns:SectionKindType" maxOccurs="1" minOccurs="0"></element>
-            <element name="static" type="tns:SectionKindType" maxOccurs="1" minOccurs="0"></element>
-            <element name="dynamic" type="tns:SectionKindType" maxOccurs="1" minOccurs="0"></element>
-        </all>
+        <sequence>
+            <choice maxOccurs="unbounded">
+                <element name="controls" type="tns:SectionKindType" maxOccurs="unbounded" minOccurs="0"></element>
+                <element name="static" type="tns:SectionKindType" maxOccurs="unbounded" minOccurs="0"></element>
+                <element name="dynamic" type="tns:SectionKindType" maxOccurs="unbounded" minOccurs="0"></element>
+            </choice>
+        </sequence>
         <attribute name="name" type="string" use="required"></attribute>
     </complexType>