Camera2: Fix metadata XML to maintain ordering

Change-Id: I69adfbb1010b5f624d3ee618a1a45e0be510ca31
diff --git a/camera/docs/metadata_helpers.py b/camera/docs/metadata_helpers.py
index 1a01dff..35e9ba4 100644
--- a/camera/docs/metadata_helpers.py
+++ b/camera/docs/metadata_helpers.py
@@ -19,6 +19,7 @@
 """
 
 import metadata_model
+from collections import OrderedDict
 
 _context_buf = None
 
@@ -76,7 +77,7 @@
      not isinstance(node, metadata_model.InnerNamespace):
       raise TypeError("expected node to be a Section or InnerNamespace")
 
-  d = {}
+  d = OrderedDict()
   # remove the 'kinds' from the path between sec and the closest entries
   # then search the immediate children of the search path
   search_path = isinstance(node, metadata_model.Section) and node.kinds \
diff --git a/camera/docs/metadata_model.py b/camera/docs/metadata_model.py
index 1acf2b5..e91fcd1 100644
--- a/camera/docs/metadata_model.py
+++ b/camera/docs/metadata_model.py
@@ -34,6 +34,7 @@
 """
 
 import sys
+from collections import OrderedDict
 
 class Node(object):
   """
@@ -152,7 +153,7 @@
 
   @staticmethod
   def _dictionary_by_name(values):
-    d = {}
+    d = OrderedDict()
     for i in values:
       d[i.name] = i
 
@@ -205,6 +206,7 @@
     self._entries = []
     # kind => { name => entry }
     self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} }
+    self._entries_ordered = [] # list of ordered Entry/Clone instances
     self._clones = []
 
 # Public (Read Only)
@@ -265,6 +267,7 @@
     e = Entry(**entry)
     self._entries.append(e)
     self._entry_map[e.kind][e.name] = e
+    self._entries_ordered.append(e)
 
   def insert_clone(self, clone):
     """
@@ -285,6 +288,7 @@
     c = Clone(entry, **clone)
     self._entry_map[c.kind][c.name] = c
     self._clones.append(c)
+    self._entries_ordered.append(c)
 
   def prune_clones(self):
     """
@@ -311,6 +315,7 @@
       # remove from global list
       self._clones.remove(p)
       self._entry_map[p.kind].pop(p.name)
+      self._entries_ordered.remove(p)
 
 
   # After all entries/clones are inserted,
@@ -367,7 +372,7 @@
     for ons_name, ons in root.iteritems():
       ons._leafs = []
 
-    for p in self._get_properties():
+    for p in self._entries_ordered:
       ons_name = p.get_outer_namespace()
       ons = root.get(ons_name, OuterNamespace(ons_name, self))
       root[ons_name] = ons
@@ -380,7 +385,6 @@
       ons.validate_tree()
 
       self._construct_sections(ons)
-      ons.sort_children()
 
       if ons not in self._outer_namespaces:
         self._outer_namespaces.append(ons)
@@ -414,7 +418,6 @@
                              %(sec)
 
       self._construct_kinds(sec)
-      sec.sort_children()
 
       if sec not in outer_namespace.sections:
         outer_namespace._sections.append(sec)
@@ -457,7 +460,6 @@
       self._construct_inner_namespaces(kind)
       kind.validate_tree()
       self._construct_entries(kind)
-      kind.sort_children()
       kind.validate_tree()
 
       if kind not in section.kinds:
@@ -495,7 +497,6 @@
       ins.validate_tree()
       # construct children entries
       self._construct_entries(ins, depth + 1)
-      ins.sort_children()
 
       if ins not in parent.namespaces:
         parent._namespaces.append(ins)
diff --git a/camera/docs/metadata_parser_xml.py b/camera/docs/metadata_parser_xml.py
index a9ee25f..cadbac1 100755
--- a/camera/docs/metadata_parser_xml.py
+++ b/camera/docs/metadata_parser_xml.py
@@ -107,34 +107,36 @@
       for tag in tags.find_all('tag'):
         self.metadata.insert_tag(tag['id'], tag.string)
 
-    for entry in self.soup.find_all("entry"):
-      d = {
-         'name': fully_qualified_name(entry),
-         'type': entry['type'],
-         'kind': find_kind(entry),
-         'type_notes': entry.attrs.get('type_notes')
-      }
+    # add all entries, preserving the ordering of the XML file
+    # this is important for future ABI compatibility when generating code
+    entry_filter = lambda x: x.name == 'entry' or x.name == 'clone'
+    for entry in self.soup.find_all(entry_filter):
+      if entry.name == 'entry':
+        d = {
+              'name': fully_qualified_name(entry),
+              'type': entry['type'],
+              'kind': find_kind(entry),
+              'type_notes': entry.attrs.get('type_notes')
+            }
 
-      d2 = self._parse_entry(entry)
+        d2 = self._parse_entry(entry)
+        insert = self.metadata.insert_entry
+      else:
+        d = {
+           'name': entry['entry'],
+           'kind': find_kind(entry),
+           'target_kind': entry['kind'],
+          # no type since its the same
+          # no type_notes since its the same
+        }
+        d2 = {}
+
+        insert = self.metadata.insert_clone
+
       d3 = self._parse_entry_optional(entry)
 
       entry_dict = dict(d.items() + d2.items() + d3.items())
-      self.metadata.insert_entry(entry_dict)
-
-    entry = None
-
-    for clone in self.soup.find_all("clone"):
-      d = {
-         'name': clone['entry'],
-         'kind': find_kind(clone),
-         'target_kind': clone['kind'],
-        # no type since its the same
-        # no type_notes since its the same
-      }
-
-      d2 = self._parse_entry_optional(clone)
-      clone_dict = dict(d.items() + d2.items())
-      self.metadata.insert_clone(clone_dict)
+      insert(entry_dict)
 
     self.metadata.construct_graph()