xref: /aosp_15_r20/system/media/camera/docs/metadata_parser_xml.py (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
1*b9df5ad1SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*b9df5ad1SAndroid Build Coastguard Worker
3*b9df5ad1SAndroid Build Coastguard Worker#
4*b9df5ad1SAndroid Build Coastguard Worker# Copyright (C) 2012 The Android Open Source Project
5*b9df5ad1SAndroid Build Coastguard Worker#
6*b9df5ad1SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
7*b9df5ad1SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
8*b9df5ad1SAndroid Build Coastguard Worker# You may obtain a copy of the License at
9*b9df5ad1SAndroid Build Coastguard Worker#
10*b9df5ad1SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
11*b9df5ad1SAndroid Build Coastguard Worker#
12*b9df5ad1SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
13*b9df5ad1SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
14*b9df5ad1SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*b9df5ad1SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
16*b9df5ad1SAndroid Build Coastguard Worker# limitations under the License.
17*b9df5ad1SAndroid Build Coastguard Worker#
18*b9df5ad1SAndroid Build Coastguard Worker
19*b9df5ad1SAndroid Build Coastguard Worker"""
20*b9df5ad1SAndroid Build Coastguard WorkerA parser for metadata_definitions.xml can also render the resulting model
21*b9df5ad1SAndroid Build Coastguard Workerover a Mako template.
22*b9df5ad1SAndroid Build Coastguard Worker
23*b9df5ad1SAndroid Build Coastguard WorkerUsage:
24*b9df5ad1SAndroid Build Coastguard Worker  metadata_parser_xml.py <filename.xml> <template.mako> [<output_file>]
25*b9df5ad1SAndroid Build Coastguard Worker  - outputs the resulting template to output_file (stdout if none specified)
26*b9df5ad1SAndroid Build Coastguard Worker
27*b9df5ad1SAndroid Build Coastguard WorkerModule:
28*b9df5ad1SAndroid Build Coastguard Worker  The parser is also available as a module import (MetadataParserXml) to use
29*b9df5ad1SAndroid Build Coastguard Worker  in other modules.
30*b9df5ad1SAndroid Build Coastguard Worker
31*b9df5ad1SAndroid Build Coastguard WorkerDependencies:
32*b9df5ad1SAndroid Build Coastguard Worker  BeautifulSoup - an HTML/XML parser available to download from
33*b9df5ad1SAndroid Build Coastguard Worker          http://www.crummy.com/software/BeautifulSoup/
34*b9df5ad1SAndroid Build Coastguard Worker  Mako - a template engine for Python, available to download from
35*b9df5ad1SAndroid Build Coastguard Worker     http://www.makotemplates.org/
36*b9df5ad1SAndroid Build Coastguard Worker"""
37*b9df5ad1SAndroid Build Coastguard Worker
38*b9df5ad1SAndroid Build Coastguard Workerimport sys
39*b9df5ad1SAndroid Build Coastguard Workerimport os
40*b9df5ad1SAndroid Build Coastguard Worker
41*b9df5ad1SAndroid Build Coastguard Workerfrom bs4 import BeautifulSoup
42*b9df5ad1SAndroid Build Coastguard Workerfrom bs4 import NavigableString
43*b9df5ad1SAndroid Build Coastguard Worker
44*b9df5ad1SAndroid Build Coastguard Workerfrom datetime import datetime
45*b9df5ad1SAndroid Build Coastguard Worker
46*b9df5ad1SAndroid Build Coastguard Workerfrom io import StringIO
47*b9df5ad1SAndroid Build Coastguard Worker
48*b9df5ad1SAndroid Build Coastguard Workerfrom mako.template import Template
49*b9df5ad1SAndroid Build Coastguard Workerfrom mako.lookup import TemplateLookup
50*b9df5ad1SAndroid Build Coastguard Workerfrom mako.runtime import Context
51*b9df5ad1SAndroid Build Coastguard Worker
52*b9df5ad1SAndroid Build Coastguard Workerfrom metadata_model import *
53*b9df5ad1SAndroid Build Coastguard Workerimport metadata_model
54*b9df5ad1SAndroid Build Coastguard Workerfrom metadata_validate import *
55*b9df5ad1SAndroid Build Coastguard Workerimport metadata_helpers
56*b9df5ad1SAndroid Build Coastguard Worker
57*b9df5ad1SAndroid Build Coastguard Workerclass MetadataParserXml:
58*b9df5ad1SAndroid Build Coastguard Worker  """
59*b9df5ad1SAndroid Build Coastguard Worker  A class to parse any XML block that passes validation with metadata-validate.
60*b9df5ad1SAndroid Build Coastguard Worker  It builds a metadata_model.Metadata graph and then renders it over a
61*b9df5ad1SAndroid Build Coastguard Worker  Mako template.
62*b9df5ad1SAndroid Build Coastguard Worker
63*b9df5ad1SAndroid Build Coastguard Worker  Attributes (Read-Only):
64*b9df5ad1SAndroid Build Coastguard Worker    soup: an instance of BeautifulSoup corresponding to the XML contents
65*b9df5ad1SAndroid Build Coastguard Worker    metadata: a constructed instance of metadata_model.Metadata
66*b9df5ad1SAndroid Build Coastguard Worker  """
67*b9df5ad1SAndroid Build Coastguard Worker  def __init__(self, xml, file_name):
68*b9df5ad1SAndroid Build Coastguard Worker    """
69*b9df5ad1SAndroid Build Coastguard Worker    Construct a new MetadataParserXml, immediately try to parse it into a
70*b9df5ad1SAndroid Build Coastguard Worker    metadata model.
71*b9df5ad1SAndroid Build Coastguard Worker
72*b9df5ad1SAndroid Build Coastguard Worker    Args:
73*b9df5ad1SAndroid Build Coastguard Worker      xml: The XML block to use for the metadata
74*b9df5ad1SAndroid Build Coastguard Worker      file_name: Source of the XML block, only for debugging/errors
75*b9df5ad1SAndroid Build Coastguard Worker
76*b9df5ad1SAndroid Build Coastguard Worker    Raises:
77*b9df5ad1SAndroid Build Coastguard Worker      ValueError: if the XML block failed to pass metadata_validate.py
78*b9df5ad1SAndroid Build Coastguard Worker    """
79*b9df5ad1SAndroid Build Coastguard Worker    self._soup = validate_xml(xml)
80*b9df5ad1SAndroid Build Coastguard Worker
81*b9df5ad1SAndroid Build Coastguard Worker    if self._soup is None:
82*b9df5ad1SAndroid Build Coastguard Worker      raise ValueError("%s has an invalid XML file" % (file_name))
83*b9df5ad1SAndroid Build Coastguard Worker
84*b9df5ad1SAndroid Build Coastguard Worker    self._metadata = Metadata()
85*b9df5ad1SAndroid Build Coastguard Worker    self._parse()
86*b9df5ad1SAndroid Build Coastguard Worker    self._metadata.construct_graph()
87*b9df5ad1SAndroid Build Coastguard Worker
88*b9df5ad1SAndroid Build Coastguard Worker  @staticmethod
89*b9df5ad1SAndroid Build Coastguard Worker  def create_from_file(file_name):
90*b9df5ad1SAndroid Build Coastguard Worker    """
91*b9df5ad1SAndroid Build Coastguard Worker    Construct a new MetadataParserXml by loading and parsing an XML file.
92*b9df5ad1SAndroid Build Coastguard Worker
93*b9df5ad1SAndroid Build Coastguard Worker    Args:
94*b9df5ad1SAndroid Build Coastguard Worker      file_name: Name of the XML file to load and parse.
95*b9df5ad1SAndroid Build Coastguard Worker
96*b9df5ad1SAndroid Build Coastguard Worker    Raises:
97*b9df5ad1SAndroid Build Coastguard Worker      ValueError: if the XML file failed to pass metadata_validate.py
98*b9df5ad1SAndroid Build Coastguard Worker
99*b9df5ad1SAndroid Build Coastguard Worker    Returns:
100*b9df5ad1SAndroid Build Coastguard Worker      MetadataParserXml instance representing the XML file.
101*b9df5ad1SAndroid Build Coastguard Worker    """
102*b9df5ad1SAndroid Build Coastguard Worker    return MetadataParserXml(open(file_name).read(), file_name)
103*b9df5ad1SAndroid Build Coastguard Worker
104*b9df5ad1SAndroid Build Coastguard Worker  @property
105*b9df5ad1SAndroid Build Coastguard Worker  def soup(self):
106*b9df5ad1SAndroid Build Coastguard Worker    return self._soup
107*b9df5ad1SAndroid Build Coastguard Worker
108*b9df5ad1SAndroid Build Coastguard Worker  @property
109*b9df5ad1SAndroid Build Coastguard Worker  def metadata(self):
110*b9df5ad1SAndroid Build Coastguard Worker    return self._metadata
111*b9df5ad1SAndroid Build Coastguard Worker
112*b9df5ad1SAndroid Build Coastguard Worker  @staticmethod
113*b9df5ad1SAndroid Build Coastguard Worker  def _find_direct_strings(element):
114*b9df5ad1SAndroid Build Coastguard Worker    if element.string is not None:
115*b9df5ad1SAndroid Build Coastguard Worker      return [element.string]
116*b9df5ad1SAndroid Build Coastguard Worker
117*b9df5ad1SAndroid Build Coastguard Worker    return [i for i in element.contents if isinstance(i, NavigableString)]
118*b9df5ad1SAndroid Build Coastguard Worker
119*b9df5ad1SAndroid Build Coastguard Worker  @staticmethod
120*b9df5ad1SAndroid Build Coastguard Worker  def _strings_no_nl(element):
121*b9df5ad1SAndroid Build Coastguard Worker    return "".join([i.strip() for i in MetadataParserXml._find_direct_strings(element)])
122*b9df5ad1SAndroid Build Coastguard Worker
123*b9df5ad1SAndroid Build Coastguard Worker  def _parse(self):
124*b9df5ad1SAndroid Build Coastguard Worker
125*b9df5ad1SAndroid Build Coastguard Worker    tags = self.soup.tags
126*b9df5ad1SAndroid Build Coastguard Worker    if tags is not None:
127*b9df5ad1SAndroid Build Coastguard Worker      for tag in tags.find_all('tag'):
128*b9df5ad1SAndroid Build Coastguard Worker        self.metadata.insert_tag(tag['id'], tag.string)
129*b9df5ad1SAndroid Build Coastguard Worker
130*b9df5ad1SAndroid Build Coastguard Worker    types = self.soup.types
131*b9df5ad1SAndroid Build Coastguard Worker    if types is not None:
132*b9df5ad1SAndroid Build Coastguard Worker      for tp in types.find_all('typedef'):
133*b9df5ad1SAndroid Build Coastguard Worker        languages = {}
134*b9df5ad1SAndroid Build Coastguard Worker        for lang in tp.find_all('language'):
135*b9df5ad1SAndroid Build Coastguard Worker          languages[lang['name']] = lang.string
136*b9df5ad1SAndroid Build Coastguard Worker
137*b9df5ad1SAndroid Build Coastguard Worker        self.metadata.insert_type(tp['name'], 'typedef', languages=languages)
138*b9df5ad1SAndroid Build Coastguard Worker
139*b9df5ad1SAndroid Build Coastguard Worker    # add all entries, preserving the ordering of the XML file
140*b9df5ad1SAndroid Build Coastguard Worker    # this is important for future ABI compatibility when generating code
141*b9df5ad1SAndroid Build Coastguard Worker    entry_filter = lambda x: x.name == 'entry' or x.name == 'clone'
142*b9df5ad1SAndroid Build Coastguard Worker    for entry in self.soup.find_all(entry_filter):
143*b9df5ad1SAndroid Build Coastguard Worker      if entry.name == 'entry':
144*b9df5ad1SAndroid Build Coastguard Worker        d = {
145*b9df5ad1SAndroid Build Coastguard Worker              'name': fully_qualified_name(entry),
146*b9df5ad1SAndroid Build Coastguard Worker              'type': entry['type'],
147*b9df5ad1SAndroid Build Coastguard Worker              'kind': find_kind(entry),
148*b9df5ad1SAndroid Build Coastguard Worker              'type_notes': entry.attrs.get('type_notes')
149*b9df5ad1SAndroid Build Coastguard Worker            }
150*b9df5ad1SAndroid Build Coastguard Worker
151*b9df5ad1SAndroid Build Coastguard Worker        d2 = self._parse_entry(entry)
152*b9df5ad1SAndroid Build Coastguard Worker        insert = self.metadata.insert_entry
153*b9df5ad1SAndroid Build Coastguard Worker      else:
154*b9df5ad1SAndroid Build Coastguard Worker        d = {
155*b9df5ad1SAndroid Build Coastguard Worker           'name': entry['entry'],
156*b9df5ad1SAndroid Build Coastguard Worker           'kind': find_kind(entry),
157*b9df5ad1SAndroid Build Coastguard Worker           'target_kind': entry['kind'],
158*b9df5ad1SAndroid Build Coastguard Worker          # no type since its the same
159*b9df5ad1SAndroid Build Coastguard Worker          # no type_notes since its the same
160*b9df5ad1SAndroid Build Coastguard Worker        }
161*b9df5ad1SAndroid Build Coastguard Worker        d2 = {}
162*b9df5ad1SAndroid Build Coastguard Worker        if 'hal_version' in entry.attrs:
163*b9df5ad1SAndroid Build Coastguard Worker          d2['hal_version'] = entry['hal_version']
164*b9df5ad1SAndroid Build Coastguard Worker
165*b9df5ad1SAndroid Build Coastguard Worker        insert = self.metadata.insert_clone
166*b9df5ad1SAndroid Build Coastguard Worker
167*b9df5ad1SAndroid Build Coastguard Worker      d3 = self._parse_entry_optional(entry)
168*b9df5ad1SAndroid Build Coastguard Worker
169*b9df5ad1SAndroid Build Coastguard Worker      entry_dict = {**d, **d2, **d3}
170*b9df5ad1SAndroid Build Coastguard Worker      insert(entry_dict)
171*b9df5ad1SAndroid Build Coastguard Worker
172*b9df5ad1SAndroid Build Coastguard Worker    self.metadata.construct_graph()
173*b9df5ad1SAndroid Build Coastguard Worker
174*b9df5ad1SAndroid Build Coastguard Worker  def _parse_entry(self, entry):
175*b9df5ad1SAndroid Build Coastguard Worker    d = {}
176*b9df5ad1SAndroid Build Coastguard Worker
177*b9df5ad1SAndroid Build Coastguard Worker    #
178*b9df5ad1SAndroid Build Coastguard Worker    # Visibility
179*b9df5ad1SAndroid Build Coastguard Worker    #
180*b9df5ad1SAndroid Build Coastguard Worker    d['visibility'] = entry.get('visibility')
181*b9df5ad1SAndroid Build Coastguard Worker
182*b9df5ad1SAndroid Build Coastguard Worker    #
183*b9df5ad1SAndroid Build Coastguard Worker    # Synthetic ?
184*b9df5ad1SAndroid Build Coastguard Worker    #
185*b9df5ad1SAndroid Build Coastguard Worker    d['synthetic'] = entry.get('synthetic') == 'true'
186*b9df5ad1SAndroid Build Coastguard Worker
187*b9df5ad1SAndroid Build Coastguard Worker    #
188*b9df5ad1SAndroid Build Coastguard Worker    # Permission needed ?
189*b9df5ad1SAndroid Build Coastguard Worker    #
190*b9df5ad1SAndroid Build Coastguard Worker    d['permission_needed'] = entry.get('permission_needed')
191*b9df5ad1SAndroid Build Coastguard Worker
192*b9df5ad1SAndroid Build Coastguard Worker    # Aconfig flag gating this entry ?
193*b9df5ad1SAndroid Build Coastguard Worker    d['aconfig_flag'] = entry.get('aconfig_flag')
194*b9df5ad1SAndroid Build Coastguard Worker
195*b9df5ad1SAndroid Build Coastguard Worker    #
196*b9df5ad1SAndroid Build Coastguard Worker    # Hardware Level (one of limited, legacy, full)
197*b9df5ad1SAndroid Build Coastguard Worker    #
198*b9df5ad1SAndroid Build Coastguard Worker    d['hwlevel'] = entry.get('hwlevel')
199*b9df5ad1SAndroid Build Coastguard Worker
200*b9df5ad1SAndroid Build Coastguard Worker    #
201*b9df5ad1SAndroid Build Coastguard Worker    # Deprecated ?
202*b9df5ad1SAndroid Build Coastguard Worker    #
203*b9df5ad1SAndroid Build Coastguard Worker    d['deprecated'] = entry.get('deprecated') == 'true'
204*b9df5ad1SAndroid Build Coastguard Worker
205*b9df5ad1SAndroid Build Coastguard Worker    #
206*b9df5ad1SAndroid Build Coastguard Worker    # Optional for non-full hardware level devices
207*b9df5ad1SAndroid Build Coastguard Worker    #
208*b9df5ad1SAndroid Build Coastguard Worker    d['optional'] = entry.get('optional') == 'true'
209*b9df5ad1SAndroid Build Coastguard Worker
210*b9df5ad1SAndroid Build Coastguard Worker    #
211*b9df5ad1SAndroid Build Coastguard Worker    # Typedef
212*b9df5ad1SAndroid Build Coastguard Worker    #
213*b9df5ad1SAndroid Build Coastguard Worker    d['type_name'] = entry.get('typedef')
214*b9df5ad1SAndroid Build Coastguard Worker
215*b9df5ad1SAndroid Build Coastguard Worker    #
216*b9df5ad1SAndroid Build Coastguard Worker    # Initial HIDL HAL version the entry was added in
217*b9df5ad1SAndroid Build Coastguard Worker    d['hal_version'] = entry.get('hal_version')
218*b9df5ad1SAndroid Build Coastguard Worker
219*b9df5ad1SAndroid Build Coastguard Worker    #
220*b9df5ad1SAndroid Build Coastguard Worker    # HAL version from which this entry became a session characteristic ?
221*b9df5ad1SAndroid Build Coastguard Worker    d['session_characteristics_key_since'] = entry.get('session_characteristics_key_since')
222*b9df5ad1SAndroid Build Coastguard Worker
223*b9df5ad1SAndroid Build Coastguard Worker    #
224*b9df5ad1SAndroid Build Coastguard Worker    # Enum
225*b9df5ad1SAndroid Build Coastguard Worker    #
226*b9df5ad1SAndroid Build Coastguard Worker    if entry.get('enum', 'false') == 'true':
227*b9df5ad1SAndroid Build Coastguard Worker
228*b9df5ad1SAndroid Build Coastguard Worker      enum_values = []
229*b9df5ad1SAndroid Build Coastguard Worker      enum_deprecateds = []
230*b9df5ad1SAndroid Build Coastguard Worker      enum_optionals = []
231*b9df5ad1SAndroid Build Coastguard Worker      enum_visibilities = {}
232*b9df5ad1SAndroid Build Coastguard Worker      enum_notes = {}
233*b9df5ad1SAndroid Build Coastguard Worker      enum_sdk_notes = {}
234*b9df5ad1SAndroid Build Coastguard Worker      enum_ndk_notes = {}
235*b9df5ad1SAndroid Build Coastguard Worker      enum_ids = {}
236*b9df5ad1SAndroid Build Coastguard Worker      enum_hal_versions = {}
237*b9df5ad1SAndroid Build Coastguard Worker      enum_aconfig_flags = {}
238*b9df5ad1SAndroid Build Coastguard Worker      for value in entry.enum.find_all('value'):
239*b9df5ad1SAndroid Build Coastguard Worker
240*b9df5ad1SAndroid Build Coastguard Worker        value_body = self._strings_no_nl(value)
241*b9df5ad1SAndroid Build Coastguard Worker        enum_values.append(value_body)
242*b9df5ad1SAndroid Build Coastguard Worker
243*b9df5ad1SAndroid Build Coastguard Worker        if value.attrs.get('deprecated', 'false') == 'true':
244*b9df5ad1SAndroid Build Coastguard Worker          enum_deprecateds.append(value_body)
245*b9df5ad1SAndroid Build Coastguard Worker
246*b9df5ad1SAndroid Build Coastguard Worker        if value.attrs.get('optional', 'false') == 'true':
247*b9df5ad1SAndroid Build Coastguard Worker          enum_optionals.append(value_body)
248*b9df5ad1SAndroid Build Coastguard Worker
249*b9df5ad1SAndroid Build Coastguard Worker        visibility = value.attrs.get('visibility')
250*b9df5ad1SAndroid Build Coastguard Worker        if visibility is not None:
251*b9df5ad1SAndroid Build Coastguard Worker          enum_visibilities[value_body] = visibility
252*b9df5ad1SAndroid Build Coastguard Worker
253*b9df5ad1SAndroid Build Coastguard Worker        notes = value.find('notes')
254*b9df5ad1SAndroid Build Coastguard Worker        if notes is not None:
255*b9df5ad1SAndroid Build Coastguard Worker          enum_notes[value_body] = notes.string
256*b9df5ad1SAndroid Build Coastguard Worker
257*b9df5ad1SAndroid Build Coastguard Worker        sdk_notes = value.find('sdk_notes')
258*b9df5ad1SAndroid Build Coastguard Worker        if sdk_notes is not None:
259*b9df5ad1SAndroid Build Coastguard Worker          enum_sdk_notes[value_body] = sdk_notes.string
260*b9df5ad1SAndroid Build Coastguard Worker
261*b9df5ad1SAndroid Build Coastguard Worker        ndk_notes = value.find('ndk_notes')
262*b9df5ad1SAndroid Build Coastguard Worker        if ndk_notes is not None:
263*b9df5ad1SAndroid Build Coastguard Worker          enum_ndk_notes[value_body] = ndk_notes.string
264*b9df5ad1SAndroid Build Coastguard Worker
265*b9df5ad1SAndroid Build Coastguard Worker        if value.attrs.get('id') is not None:
266*b9df5ad1SAndroid Build Coastguard Worker          enum_ids[value_body] = value['id']
267*b9df5ad1SAndroid Build Coastguard Worker
268*b9df5ad1SAndroid Build Coastguard Worker        if value.attrs.get('hal_version') is not None:
269*b9df5ad1SAndroid Build Coastguard Worker          enum_hal_versions[value_body] = value['hal_version']
270*b9df5ad1SAndroid Build Coastguard Worker
271*b9df5ad1SAndroid Build Coastguard Worker        if value.attrs.get('aconfig_flag') is not None:
272*b9df5ad1SAndroid Build Coastguard Worker          enum_aconfig_flags[value_body] = value['aconfig_flag']
273*b9df5ad1SAndroid Build Coastguard Worker
274*b9df5ad1SAndroid Build Coastguard Worker      d['enum_values'] = enum_values
275*b9df5ad1SAndroid Build Coastguard Worker      d['enum_deprecateds'] = enum_deprecateds
276*b9df5ad1SAndroid Build Coastguard Worker      d['enum_optionals'] = enum_optionals
277*b9df5ad1SAndroid Build Coastguard Worker      d['enum_visibilities'] = enum_visibilities
278*b9df5ad1SAndroid Build Coastguard Worker      d['enum_notes'] = enum_notes
279*b9df5ad1SAndroid Build Coastguard Worker      d['enum_sdk_notes'] = enum_sdk_notes
280*b9df5ad1SAndroid Build Coastguard Worker      d['enum_ndk_notes'] = enum_ndk_notes
281*b9df5ad1SAndroid Build Coastguard Worker      d['enum_ids'] = enum_ids
282*b9df5ad1SAndroid Build Coastguard Worker      d['enum_hal_versions'] = enum_hal_versions
283*b9df5ad1SAndroid Build Coastguard Worker      d['enum_aconfig_flags'] = enum_aconfig_flags
284*b9df5ad1SAndroid Build Coastguard Worker      d['enum'] = True
285*b9df5ad1SAndroid Build Coastguard Worker
286*b9df5ad1SAndroid Build Coastguard Worker    #
287*b9df5ad1SAndroid Build Coastguard Worker    # Container (Array/Tuple)
288*b9df5ad1SAndroid Build Coastguard Worker    #
289*b9df5ad1SAndroid Build Coastguard Worker    if entry.attrs.get('container') is not None:
290*b9df5ad1SAndroid Build Coastguard Worker      container_name = entry['container']
291*b9df5ad1SAndroid Build Coastguard Worker
292*b9df5ad1SAndroid Build Coastguard Worker      array = entry.find('array')
293*b9df5ad1SAndroid Build Coastguard Worker      if array is not None:
294*b9df5ad1SAndroid Build Coastguard Worker        array_sizes = []
295*b9df5ad1SAndroid Build Coastguard Worker        for size in array.find_all('size'):
296*b9df5ad1SAndroid Build Coastguard Worker          array_sizes.append(size.string)
297*b9df5ad1SAndroid Build Coastguard Worker        d['container_sizes'] = array_sizes
298*b9df5ad1SAndroid Build Coastguard Worker
299*b9df5ad1SAndroid Build Coastguard Worker      tupl = entry.find('tuple')
300*b9df5ad1SAndroid Build Coastguard Worker      if tupl is not None:
301*b9df5ad1SAndroid Build Coastguard Worker        tupl_values = []
302*b9df5ad1SAndroid Build Coastguard Worker        for val in tupl.find_all('value'):
303*b9df5ad1SAndroid Build Coastguard Worker          tupl_values.append(val.name)
304*b9df5ad1SAndroid Build Coastguard Worker        d['tuple_values'] = tupl_values
305*b9df5ad1SAndroid Build Coastguard Worker        d['container_sizes'] = len(tupl_values)
306*b9df5ad1SAndroid Build Coastguard Worker
307*b9df5ad1SAndroid Build Coastguard Worker      d['container'] = container_name
308*b9df5ad1SAndroid Build Coastguard Worker
309*b9df5ad1SAndroid Build Coastguard Worker    return d
310*b9df5ad1SAndroid Build Coastguard Worker
311*b9df5ad1SAndroid Build Coastguard Worker  def _parse_entry_optional(self, entry):
312*b9df5ad1SAndroid Build Coastguard Worker    d = {}
313*b9df5ad1SAndroid Build Coastguard Worker
314*b9df5ad1SAndroid Build Coastguard Worker    optional_elements = ['description', 'range', 'units', 'details', 'hal_details', 'ndk_details',\
315*b9df5ad1SAndroid Build Coastguard Worker                         'deprecation_description']
316*b9df5ad1SAndroid Build Coastguard Worker    for i in optional_elements:
317*b9df5ad1SAndroid Build Coastguard Worker      prop = find_child_tag(entry, i)
318*b9df5ad1SAndroid Build Coastguard Worker
319*b9df5ad1SAndroid Build Coastguard Worker      if prop is not None:
320*b9df5ad1SAndroid Build Coastguard Worker        d[i] = prop.string
321*b9df5ad1SAndroid Build Coastguard Worker
322*b9df5ad1SAndroid Build Coastguard Worker    tag_ids = []
323*b9df5ad1SAndroid Build Coastguard Worker    for tag in entry.find_all('tag'):
324*b9df5ad1SAndroid Build Coastguard Worker      tag_ids.append(tag['id'])
325*b9df5ad1SAndroid Build Coastguard Worker
326*b9df5ad1SAndroid Build Coastguard Worker    d['tag_ids'] = tag_ids
327*b9df5ad1SAndroid Build Coastguard Worker
328*b9df5ad1SAndroid Build Coastguard Worker    return d
329*b9df5ad1SAndroid Build Coastguard Worker
330*b9df5ad1SAndroid Build Coastguard Worker  def render(self, template, output_name=None, enum=None,
331*b9df5ad1SAndroid Build Coastguard Worker             copyright_year=None):
332*b9df5ad1SAndroid Build Coastguard Worker    """
333*b9df5ad1SAndroid Build Coastguard Worker    Render the metadata model using a Mako template as the view.
334*b9df5ad1SAndroid Build Coastguard Worker
335*b9df5ad1SAndroid Build Coastguard Worker    The template gets the metadata as an argument, as well as all
336*b9df5ad1SAndroid Build Coastguard Worker    public attributes from the metadata_helpers module.
337*b9df5ad1SAndroid Build Coastguard Worker
338*b9df5ad1SAndroid Build Coastguard Worker    The output file is encoded with UTF-8.
339*b9df5ad1SAndroid Build Coastguard Worker
340*b9df5ad1SAndroid Build Coastguard Worker    Args:
341*b9df5ad1SAndroid Build Coastguard Worker      template: path to a Mako template file
342*b9df5ad1SAndroid Build Coastguard Worker      output_name: path to the output file, or None to use stdout
343*b9df5ad1SAndroid Build Coastguard Worker      enum: The name of the enum, if any
344*b9df5ad1SAndroid Build Coastguard Worker      copyright_year: the year in the copyright section of output file
345*b9df5ad1SAndroid Build Coastguard Worker    """
346*b9df5ad1SAndroid Build Coastguard Worker    buf = StringIO()
347*b9df5ad1SAndroid Build Coastguard Worker    metadata_helpers._context_buf = buf
348*b9df5ad1SAndroid Build Coastguard Worker    metadata_helpers._enum = enum
349*b9df5ad1SAndroid Build Coastguard Worker
350*b9df5ad1SAndroid Build Coastguard Worker    copyright_year = copyright_year \
351*b9df5ad1SAndroid Build Coastguard Worker                        if copyright_year is not None \
352*b9df5ad1SAndroid Build Coastguard Worker                        else str(datetime.now().year)
353*b9df5ad1SAndroid Build Coastguard Worker    metadata_helpers._copyright_year = \
354*b9df5ad1SAndroid Build Coastguard Worker        metadata_helpers.infer_copyright_year_from_source(output_name,
355*b9df5ad1SAndroid Build Coastguard Worker                                                          copyright_year)
356*b9df5ad1SAndroid Build Coastguard Worker
357*b9df5ad1SAndroid Build Coastguard Worker    helpers = [(i, getattr(metadata_helpers, i))
358*b9df5ad1SAndroid Build Coastguard Worker                for i in dir(metadata_helpers) if not i.startswith('_')]
359*b9df5ad1SAndroid Build Coastguard Worker    helpers = dict(helpers)
360*b9df5ad1SAndroid Build Coastguard Worker
361*b9df5ad1SAndroid Build Coastguard Worker    lookup = TemplateLookup(directories=[os.getcwd()])
362*b9df5ad1SAndroid Build Coastguard Worker    tpl = Template(filename=template, lookup=lookup)
363*b9df5ad1SAndroid Build Coastguard Worker
364*b9df5ad1SAndroid Build Coastguard Worker    ctx = Context(buf, metadata=self.metadata, **helpers)
365*b9df5ad1SAndroid Build Coastguard Worker    tpl.render_context(ctx)
366*b9df5ad1SAndroid Build Coastguard Worker
367*b9df5ad1SAndroid Build Coastguard Worker    tpl_data = buf.getvalue()
368*b9df5ad1SAndroid Build Coastguard Worker    metadata_helpers._context_buf = None
369*b9df5ad1SAndroid Build Coastguard Worker    buf.close()
370*b9df5ad1SAndroid Build Coastguard Worker
371*b9df5ad1SAndroid Build Coastguard Worker    if output_name is None:
372*b9df5ad1SAndroid Build Coastguard Worker      print(tpl_data)
373*b9df5ad1SAndroid Build Coastguard Worker    else:
374*b9df5ad1SAndroid Build Coastguard Worker      open(output_name, "w").write(tpl_data)
375*b9df5ad1SAndroid Build Coastguard Worker
376*b9df5ad1SAndroid Build Coastguard Worker#####################
377*b9df5ad1SAndroid Build Coastguard Worker#####################
378*b9df5ad1SAndroid Build Coastguard Worker
379*b9df5ad1SAndroid Build Coastguard Workerif __name__ == "__main__":
380*b9df5ad1SAndroid Build Coastguard Worker  if len(sys.argv) <= 2:
381*b9df5ad1SAndroid Build Coastguard Worker    print("Usage: %s <filename.xml> <template.mako> [<output_file>]"\
382*b9df5ad1SAndroid Build Coastguard Worker          " [<copyright_year>]" \
383*b9df5ad1SAndroid Build Coastguard Worker          % (sys.argv[0]), file=sys.stderr)
384*b9df5ad1SAndroid Build Coastguard Worker    sys.exit(0)
385*b9df5ad1SAndroid Build Coastguard Worker
386*b9df5ad1SAndroid Build Coastguard Worker  file_name = sys.argv[1]
387*b9df5ad1SAndroid Build Coastguard Worker  template_name = sys.argv[2]
388*b9df5ad1SAndroid Build Coastguard Worker  output_name = sys.argv[3] if len(sys.argv) > 3 else None
389*b9df5ad1SAndroid Build Coastguard Worker  copyright_year = sys.argv[4] if len(sys.argv) > 4 else str(datetime.now().year)
390*b9df5ad1SAndroid Build Coastguard Worker
391*b9df5ad1SAndroid Build Coastguard Worker  parser = MetadataParserXml.create_from_file(file_name)
392*b9df5ad1SAndroid Build Coastguard Worker  parser.render(template_name, output_name, None, copyright_year)
393*b9df5ad1SAndroid Build Coastguard Worker
394*b9df5ad1SAndroid Build Coastguard Worker  sys.exit(0)
395