Camera: Metadata docs HTML generated from XML: DO NOT MERGE

Change-Id: Ic39249d7019dab0a6c5f5daf5d54074f6cc9c0d4
diff --git a/camera/docs/html.mako b/camera/docs/html.mako
new file mode 100644
index 0000000..a3a9042
--- /dev/null
+++ b/camera/docs/html.mako
@@ -0,0 +1,275 @@
+## -*- coding: utf-8 -*-
+<!DOCTYPE html>
+<html>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<head>
+  <meta charset="utf-8" />
+  <title>Android Camera HAL2.0 Properties</title>
+  <style type="text/css">
+    .section { font-size: 1.5em; font-weight: bold; background-color: beige; padding: 0.5em 0em 0.5em 0.1em }
+    .kind { font-size: 1.2em; font-weight: bold; padding-left: 0.5em; background-color: gray }
+    .entry { background-color: burlywood }
+
+    /* table column sizes */
+    table { table-layout: fixed; width: 100%; word-wrap: break-word }
+    td,th { border: 1px solid;  }
+    .th_name { width: 20% }
+    .th_units { width: 10% }
+    .th_tags { width: 5% }
+    .th_notes { width: 30% }
+    .th_type { width: 20% }
+    td { font-size: 0.9em; }
+
+    /* hide the first thead, we need it there only to enforce column sizes */
+    .thead_dummy { visibility: hidden; }
+
+    /* Entry flair */
+    .entry_name { font-family: monospace; font-style: italic; }
+
+    /* Entry type flair */
+    .entry_type_name { color: darkgreen; font-weight: bold; }
+    .entry_type_enum_name { font-family: monospace; font-weight: bolder; }
+    .entry_type_enum_notes:before { content:" - " }
+    .entry_type_enum_value:before { content:" = " }
+    .entry_type_enum_value { font-family: monospace; }
+    .entry ul { margin: 0 0 0 0; list-style-position: inside; padding-left: 0.5em; }
+    .entry ul li { padding: 0 0 0 0; margin: 0 0 0 0;}
+
+    /* Entry tags flair */
+    .entry_tags ul { list-style-type: none; }
+
+
+    /* TODO: generate abbr element for each tag link? */
+    /* TODO for each x.y.z try to link it to the entry */
+
+  </style>
+</head>
+
+<%!
+  # insert word break hints for the browser
+  #    e.g. X/Y/Z -> X/<wbr>Y/<wbr>/Z. also for X.Y.Z, X_Y_Z.
+  def wbr(text):
+    replace_chars=['.', '/', '_', ',']
+    new_txt = text
+    for i in replace_chars:
+      new_txt = new_txt.replace(i, i + "<wbr>")
+
+    return new_txt
+%>
+
+
+<body>
+  <h1>Android Camera HAL2.0 Properties</h1>
+
+  <h2>Table of Contents</h2>
+  <ul class="toc">
+    <li><a href="#tag_index">Tags</a></li>
+
+
+    % for section in metadata.find_all(lambda x: isinstance(x, metadata_model.Section)):
+    <li><p class="toc_section"><a href="#section_${section.name}">${section.name}</a></p>
+    <ul class="toc_section">
+      % for prop in section.find_all(lambda x: isinstance(x, metadata_model.Entry)):
+        <li><a href="#${prop.kind}_${prop.name}">${prop.name}</a> (${prop.kind})</li>
+      % endfor
+    </ul>
+    </li> <!-- toc_section -->
+    % endfor
+  </ul>
+
+  <h1>Properties</h1>
+  <table class="properties">
+
+    <thead class="thead_dummy">
+      <tr>
+        <th class="th_name">Property Name</th>
+        <th class="th_type">Type</th>
+        <th class="th_description">Description</th>
+        <th class="th_units">Units</th>
+        <th class="th_range">Range</th>
+        <th class="th_notes">Notes</th>
+        <th class="th_tags">Tags</th>
+      </tr>
+    </thead> <!-- so that the first occurrence of thead is not
+                         above the first occurrence of tr -->
+% for root in metadata.outer_namespaces:
+<!-- <namespace name="${root.name}"> -->
+  % for section in root.sections:
+  <tr><td colspan="7" id="section_${section.name}" class="section">${section.name}</td></tr>
+
+    % if section.description is not None:
+      <tr class="description"><td>${section.description}</td></tr>
+    % endif
+
+    % for kind in section.kinds: # dynamic,static,controls
+      <tr><td colspan="7" class="kind">${kind.name}</td></tr>
+
+      <thead>
+        <tr>
+          <th class="th_name">Property Name</th>
+          <th class="th_type">Type</th>
+          <th class="th_description">Description</th>
+          <th class="th_units">Units</th>
+          <th class="th_range">Range</th>
+          <th class="th_notes">Notes</th>
+          <th class="th_tags">Tags</th>
+        </tr>
+      </thead>
+
+      <tbody>
+
+        <%def name="insert_body(node)">
+            % for nested in node.namespaces:
+                ${insert_namespace(nested)}
+            % endfor
+
+            % for entry in node.merged_entries:
+                ${insert_entry(entry)}
+            % endfor
+        </%def>
+
+        <%def name="insert_namespace(namespace)">
+            ${insert_body(namespace)}
+        </%def>
+
+        <%def name="insert_entry(prop)">
+        % if False: #prop.is_clone():
+            <clone entry="${prop.name}" kind="${prop.target_kind}">
+
+              % if prop.notes is not None:
+                <notes>${prop.notes | h,wbr}</notes>
+              % endif
+
+              % for tag in prop.tags:
+                <tag id="${tag.id}" />
+              % endfor
+
+            </clone>
+        % else:
+          <tr class="entry" id="${prop.kind}_${prop.name}">
+            <td class="entry_name">${prop.name | wbr}</td>
+            <td class="entry_type">
+              <span class="entry_type_name">${prop.type}</span>
+              % if prop.container is not None:
+                <span class="entry_type_container">x</span>
+              % endif
+
+              % if prop.container == 'array':
+                <span class="entry_type_array">
+                  ${" x ".join(prop.container_sizes)}
+                </span>
+              % elif prop.container == 'tuple':
+                <ul class="entry_type_tuple">
+                % for val in prop.tuple_values:
+                  <li>${val}</li>
+                % endfor
+                </ul>
+              % endif
+
+              % if prop.type_notes is not None:
+                <div class="entry_type_notes">${prop.type_notes | wbr}</div>
+              % endif
+
+              % if prop.type == 'enum':
+                <ul class="entry_type_enum">
+                  % for value in prop.enum.values:
+                  <li>
+                    <span class="entry_type_enum_name">${value.name}</span>
+                  % if value.optional:
+                    <span class="entry_type_enum_optional">optional</span>
+                  % endif:
+                  % if value.id is not None:
+                    <span class="entry_type_enum_value">${value.id}</span>
+                  % endif
+                  % if value.notes is not None:
+                    <span class="entry_type_enum_notes">${value.notes | wbr}</span>
+                  % endif
+                  </li>
+                  % endfor
+                </ul>
+              % endif
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+            % if prop.description is not None:
+              ${prop.description | wbr}
+            % endif
+            </td>
+
+            <td class="entry_units">
+            % if prop.units is not None:
+              ${prop.units | wbr}
+            % endif
+            </td>
+
+            <td class="entry_range">
+            % if prop.range is not None:
+              ${prop.range | wbr}
+            % endif
+            </td>
+
+            <td class="entry_notes">
+            % if prop.notes is not None:
+              ${prop.notes | wbr}
+            % endif
+            </td>
+
+            <td class="entry_tags">
+            % if list(prop.tags):
+              <ul class="entry_tags">
+              % for tag in prop.tags:
+                  <li><a href="#tag_${tag.id}">${tag.id}</a></li>
+              % endfor
+              </ul>
+            % endif
+            </td>
+
+          </tr> <!-- end of entry -->
+        % endif
+        </%def>
+
+        ${insert_body(kind)}
+
+      <!-- end of kind -->
+      </tbody>
+    % endfor # for each kind
+
+  <!-- end of section -->
+  % endfor
+<!-- </namespace> -->
+% endfor
+  </table>
+
+  <div class="tags" id="tag_index">
+    <h2>Tags</h2>
+    <ul>
+    % for tag in metadata.tags:
+      <li id="tag_${tag.id}">${tag.id} - ${tag.description}
+        <ul class="tags_entries">
+        % for prop in tag.entries:
+          <li><a href="#${prop.kind}_${prop.name}">${prop.name}</a> (${prop.kind})</li>
+        % endfor
+        </ul>
+      </li> <!-- tag_${tag.id} -->
+    % endfor
+    </ul>
+  </div>
+
+  [ <a href="#">top</a> ]
+
+</body>
+</html>
diff --git a/camera/docs/metadata-parser-sanity-check b/camera/docs/metadata-parser-sanity-check
index dfb83d0..e47ec0b 100755
--- a/camera/docs/metadata-parser-sanity-check
+++ b/camera/docs/metadata-parser-sanity-check
@@ -34,7 +34,7 @@
 tmp_tidy1=$(mktemp)
 tmp_tidy2=$(mktemp)
 
-$thisdir/metadata_parser_xml.py $thisdir/metadata_properties.xml  > $tmp_out
+$thisdir/metadata_parser_xml.py $thisdir/metadata_properties.xml $thisdir/metadata_template.mako > $tmp_out
 tidy -indent -xml -quiet $thisdir/metadata_properties.xml > $tmp_tidy1
 tidy -indent -xml -quiet $tmp_out > $tmp_tidy2
 
diff --git a/camera/docs/metadata_model.py b/camera/docs/metadata_model.py
index 03dacad..b6215ae 100644
--- a/camera/docs/metadata_model.py
+++ b/camera/docs/metadata_model.py
@@ -342,6 +342,9 @@
         if tag not in p._tags:
           p._tags.append(tag)
 
+        if p not in tag.entries:
+          tag._entries.append(p)
+
   def _construct_clones(self):
     for p in self._clones:
       target_kind = p.target_kind
@@ -553,7 +556,7 @@
     self._parent      = parent
 
     # all entries that have this tag, including clones
-    self._entries     = []
+    self._entries     = []  # filled in by Metadata#construct_tags
 
   @property
   def id(self):
@@ -641,6 +644,7 @@
     parent: An edge to the parent, which is always a Section  instance.
     namespaces: A sequence of InnerNamespace children.
     entries: A sequence of Entry/Clone children.
+    merged_entries: A sequence of MergedEntry virtual nodes from entries
   """
   def __init__(self, name, parent):
     self._name = name
@@ -658,6 +662,11 @@
   def entries(self):
     return self._entries
 
+  @property
+  def merged_entries(self):
+    for i in self.entries:
+      yield i.merge()
+
   def sort_children(self):
     self._namespaces.sort(key=self._get_name())
     self._entries.sort(key=self._get_name())
@@ -678,6 +687,7 @@
     parent: An edge to the parent, which is an InnerNamespace or a Kind.
     namespaces: A sequence of InnerNamespace children.
     entries: A sequence of Entry/Clone children.
+    merged_entries: A sequence of MergedEntry virtual nodes from entries
   """
   def __init__(self, name, parent):
     self._name        = name
@@ -694,6 +704,11 @@
   def entries(self):
     return self._entries
 
+  @property
+  def merged_entries(self):
+    for i in self.entries:
+      yield i.merge()
+
   def sort_children(self):
     self._namespaces.sort(key=self._get_name())
     self._entries.sort(key=self._get_name())
@@ -934,9 +949,18 @@
 
     if self._type == 'enum':
       self._enum = Enum(self, enum_values, enum_ids, enum_optionals, enum_notes)
+    else:
+      self._enum = None
 
     self._property_keys = kwargs
 
+  def merge(self):
+    """
+    Copy the attributes into a new entry, merging it with the target entry
+    if it's a clone.
+    """
+    return MergedEntry(self)
+
   # Helpers for accessing less than the fully qualified name
 
   def get_name_as_list(self):
@@ -1077,6 +1101,42 @@
     """
     return True
 
+class MergedEntry(Entry):
+  """
+  A MergedEntry has all the attributes of a Clone and its target Entry merged
+  together.
 
+  Remarks:
+    Useful when we want to 'unfold' a clone into a real entry by copying out
+    the target entry data. In this case we don't care about distinguishing
+    a clone vs an entry.
+  """
+  def __init__(self, entry):
+    """
+    Create a new instance of MergedEntry.
 
+    Args:
+      entry: An Entry or Clone instance
+    """
+    props_distinct = ['description', 'units', 'range', 'notes', 'tags', 'kind']
+
+    for p in props_distinct:
+      if entry.is_clone():
+        setattr(self, '_' + p, getattr(entry, p) or getattr(entry.entry, p))
+      else:
+        setattr(self, '_' + p, getattr(entry, p))
+
+    props_common = ['parent', 'name', 'name_short', 'container',
+                    'container_sizes', 'enum',
+                    'tuple_values',
+                    'type',
+                    'type_notes',
+                    'enum'
+                   ]
+
+    for p in props_common:
+      if entry.is_clone():
+        setattr(self, '_' + p, getattr(entry.entry, p))
+      else:
+        setattr(self, '_' + p, getattr(entry, p))
 
diff --git a/camera/docs/metadata_parser_xml.py b/camera/docs/metadata_parser_xml.py
index 5390cc4..05953d4 100755
--- a/camera/docs/metadata_parser_xml.py
+++ b/camera/docs/metadata_parser_xml.py
@@ -21,7 +21,7 @@
 over a Mako template.
 
 Usage:
-  metadata_parser_xml.py <filename.xml>
+  metadata_parser_xml.py <filename.xml> <template.mako>
   - outputs the resulting template to stdout
 
 Module:
@@ -43,6 +43,7 @@
 from mako.template import Template
 
 from metadata_model import *
+import metadata_model
 from metadata_validate import *
 
 class MetadataParserXml:
@@ -216,7 +217,7 @@
       output_name: path to the output file, or None to use stdout
     """
     tpl = Template(filename=template)
-    tpl_data = tpl.render(metadata=self.metadata)
+    tpl_data = tpl.render(metadata=self.metadata, metadata_model=metadata_model)
 
     if output_name is None:
       print tpl_data
@@ -228,11 +229,13 @@
 
 if __name__ == "__main__":
   if len(sys.argv) <= 1:
-    print >> sys.stderr, "Usage: %s <filename.xml>" % (sys.argv[0])
+    print >> sys.stderr, "Usage: %s <filename.xml> <template.mako>"            \
+                        % (sys.argv[0])
     sys.exit(0)
 
   file_name = sys.argv[1]
+  template_name = sys.argv[2]
   parser = MetadataParserXml(file_name)
-  parser.render("metadata_template.mako")
+  parser.render(template_name)
 
   sys.exit(0)
diff --git a/camera/docs/metadata_properties.xml b/camera/docs/metadata_properties.xml
index 360cbfc..0c80cec 100644
--- a/camera/docs/metadata_properties.xml
+++ b/camera/docs/metadata_properties.xml
@@ -89,7 +89,6 @@
           </enum>
           <description>Enum for controlling
           antibanding</description>
-          <units>Enum</units>
           <range>
           android.control.aeAvailableAntibandingModes</range>
         </entry>
@@ -114,7 +113,6 @@
           </enum>
           <description>Whether AE is currently locked to its latest
           calculated values</description>
-          <units>Enum</units>
           <notes>Note that even when AE is locked, the flash may be
           fired if the AE mode is ON_AUTO_FLASH / ON_ALWAYS_FLASH /
           ON_AUTO_FLASH_REDEYE.</notes>
@@ -146,7 +144,6 @@
           </enum>
           <description>Whether AE is currently updating the sensor
           exposure and sensitivity fields</description>
-          <units>Enum</units>
           <range>android.control.aeAvailableModes</range>
           <notes>Only effective if android.control.mode =
           AUTO</notes>
@@ -154,7 +151,8 @@
         </entry>
         <entry name="aeRegions" type="int32" container="array">
           <array>
-            <size>5 per area</size>
+            <size>5</size>
+            <size>area_count</size>
           </array>
           <description>List of areas to use for
           metering</description>
@@ -251,12 +249,12 @@
           </enum>
           <description>Whether AF is currently enabled, and what
           mode it is set to</description>
-          <units>Enum</units>
           <tag id="BC" />
         </entry>
         <entry name="afRegions" type="int32" container="array">
           <array>
-            <size>5 per area</size>
+            <size>5</size>
+            <size>area_count</size>
           </array>
           <description>List of areas to use for focus
           estimation</description>
@@ -288,7 +286,6 @@
           </enum>
           <description>Whether AWB is currently locked to its
           latest calculated values</description>
-          <units>Enum</units>
           <notes>Note that AWB lock is only meaningful for AUTO
           mode; in other modes, AWB is already fixed to a specific
           setting</notes>
@@ -309,14 +306,14 @@
           <description>Whether AWB is currently setting the color
           transform fields, and what its illumination target
           is</description>
-          <units>Enum</units>
           <notes>[BC - AWB lock,AWB modes]</notes>
           <tag id="BC" />
           <tag id="AWB" />
         </entry>
         <entry name="awbRegions" type="int32" container="array">
           <array>
-            <size>5 per area</size>
+            <size>5</size>
+            <size>area_count</size>
           </array>
           <description>List of areas to use for illuminant
           estimation</description>
@@ -363,7 +360,6 @@
           <description>Information to 3A routines about the purpose
           of this capture, to help decide optimal 3A
           strategy</description>
-          <units>Enum</units>
           <range>all must be supported</range>
           <notes>Only used if android.control.mode != OFF.</notes>
           <tag id="BC" />
@@ -382,7 +378,6 @@
           </enum>
           <description>Whether any special color effect is in use.
           Only used if android.control.mode != OFF</description>
-          <units>Enum</units>
           <range>android.control.availableEffects</range>
           <tag id="BC" />
         </entry>
@@ -408,7 +403,6 @@
           </enum>
           <description>Overall mode of 3A control
           routines</description>
-          <units>Enum</units>
           <range>all must be supported</range>
           <tag id="BC" />
         </entry>
@@ -442,7 +436,6 @@
           </enum>
           <description>Which scene mode is active when
           android.control.mode = SCENE_MODE</description>
-          <units>Enum</units>
           <range>android.control.availableSceneModes</range>
           <tag id="BC" />
         </entry>
@@ -453,7 +446,6 @@
           </enum>
           <description>Whether video stabilization is
           active</description>
-          <units>Enum</units>
           <notes>If enabled, video stabilization can modify the
           android.scaler.cropRegion to keep the video stream
           stabilized</notes>
@@ -552,7 +544,7 @@
           <tag id="BC" />
         </entry>
         <entry name="awbAvailableModes" type="byte">
-          <units>List of enums</units>
+          <units>List of enums (android.control.awbMode)</units>
           <range>OFF, AUTO must be included</range>
           <tag id="BC" />
         </entry>
@@ -631,7 +623,6 @@
             FLASH_REQUIRED as appropriate</notes></value>
           </enum>
           <description>Current state of AE algorithm</description>
-          <units>Enum</units>
           <notes>Whenever the AE algorithm state changes, a
           MSG_AUTOEXPOSURE notification must be send if a
           notification callback is registered.</notes>
@@ -667,7 +658,6 @@
             locked</notes></value>
           </enum>
           <description>Current state of AF algorithm</description>
-          <units>Enum</units>
           <notes>Whenever the AF algorithm state changes, a
           MSG_AUTOFOCUS notification must be send if a notification
           callback is registered.</notes>
@@ -698,7 +688,6 @@
             LOCKED)</notes></value>
           </enum>
           <description>Current state of AWB algorithm</description>
-          <units>Enum</units>
           <notes>Whenever the AWB algorithm state changes, a
           MSG_AUTOWHITEBALANCE notification must be send if a
           notification callback is registered.</notes>
@@ -1684,7 +1673,6 @@
             <description>Arrangement of color filters on sensor;
             represents the colors in the top-left 2x2 section of
             the sensor, in reading order</description>
-            <units>Enum</units>
             <tag id="DNG" />
           </entry>
           <entry name="exposureTimeRange" type="int64"
@@ -1892,7 +1880,6 @@
           </enum>
           <description>Light source used to define transform
           1</description>
-          <units>Enum</units>
           <notes>[EXIF LightSource tag] Must all these be
           supported? Need CCT for each!</notes>
           <tag id="DNG" />