Home | History | Annotate | Download | only in docs
      1 #!/usr/bin/python
      2 
      3 #
      4 # Copyright (C) 2012 The Android Open Source Project
      5 #
      6 # Licensed under the Apache License, Version 2.0 (the "License");
      7 # you may not use this file except in compliance with the License.
      8 # You may obtain a copy of the License at
      9 #
     10 #      http://www.apache.org/licenses/LICENSE-2.0
     11 #
     12 # Unless required by applicable law or agreed to in writing, software
     13 # distributed under the License is distributed on an "AS IS" BASIS,
     14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 # See the License for the specific language governing permissions and
     16 # limitations under the License.
     17 #
     18 
     19 """
     20 A set of classes (models) each closely representing an XML node in the
     21 metadata_definitions.xml file.
     22 
     23   Node: Base class for most nodes.
     24   Entry: A node corresponding to <entry> elements.
     25   Clone: A node corresponding to <clone> elements.
     26   MergedEntry: A node corresponding to either <entry> or <clone> elements.
     27   Kind: A node corresponding to <dynamic>, <static>, <controls> elements.
     28   InnerNamespace: A node corresponding to a <namespace> nested under a <kind>.
     29   OuterNamespace: A node corresponding to a <namespace> with <kind> children.
     30   Section: A node corresponding to a <section> element.
     31   Enum: A class corresponding an <enum> element within an <entry>
     32   EnumValue: A class corresponding to a <value> element within an Enum
     33   Metadata: Root node that also provides tree construction functionality.
     34   Tag: A node corresponding to a top level <tag> element.
     35   Typedef: A node corresponding to a <typedef> element under <types>.
     36 """
     37 
     38 import sys
     39 import itertools
     40 from collections import OrderedDict
     41 
     42 class Node(object):
     43   """
     44   Base class for most nodes that are part of the Metadata graph.
     45 
     46   Attributes (Read-Only):
     47     parent: An edge to a parent Node.
     48     name: A string describing the name, usually but not always the 'name'
     49           attribute of the corresponding XML node.
     50   """
     51 
     52   def __init__(self):
     53     self._parent = None
     54     self._name = None
     55 
     56   @property
     57   def parent(self):
     58     return self._parent
     59 
     60   @property
     61   def name(self):
     62     return self._name
     63 
     64   def find_all(self, pred):
     65     """
     66     Find all descendants that match the predicate.
     67 
     68     Args:
     69       pred: a predicate function that acts as a filter for a Node
     70 
     71     Yields:
     72       A sequence of all descendants for which pred(node) is true,
     73       in a pre-order visit order.
     74     """
     75     if pred(self):
     76       yield self
     77 
     78     if self._get_children() is None:
     79       return
     80 
     81     for i in self._get_children():
     82       for j in i.find_all(pred):
     83         yield j
     84 
     85   def find_first(self, pred):
     86     """
     87     Find the first descendant that matches the predicate.
     88 
     89     Args:
     90       pred: a predicate function that acts as a filter for a Node
     91 
     92     Returns:
     93       The first Node from find_all(pred), or None if there were no results.
     94     """
     95     for i in self.find_all(pred):
     96       return i
     97 
     98     return None
     99 
    100   def find_parent_first(self, pred):
    101     """
    102     Find the first ancestor that matches the predicate.
    103 
    104     Args:
    105       pred: A predicate function that acts as a filter for a Node
    106 
    107     Returns:
    108       The first ancestor closest to the node for which pred(node) is true.
    109     """
    110     for i in self.find_parents(pred):
    111       return i
    112 
    113     return None
    114 
    115   def find_parents(self, pred):
    116     """
    117     Find all ancestors that match the predicate.
    118 
    119     Args:
    120       pred: A predicate function that acts as a filter for a Node
    121 
    122     Yields:
    123       A sequence of all ancestors (closest to furthest) from the node,
    124       where pred(node) is true.
    125     """
    126     parent = self.parent
    127 
    128     while parent is not None:
    129       if pred(parent):
    130         yield parent
    131       parent = parent.parent
    132 
    133   def sort_children(self):
    134     """
    135     Sorts the immediate children in-place.
    136     """
    137     self._sort_by_name(self._children)
    138 
    139   def _sort_by_name(self, what):
    140     what.sort(key=lambda x: x.name)
    141 
    142   def _get_name(self):
    143     return lambda x: x.name
    144 
    145   # Iterate over all children nodes. None when node doesn't support children.
    146   def _get_children(self):
    147     return (i for i in self._children)
    148 
    149   def _children_name_map_matching(self, match=lambda x: True):
    150     d = {}
    151     for i in self._get_children():
    152       if match(i):
    153         d[i.name] = i
    154     return d
    155 
    156   @staticmethod
    157   def _dictionary_by_name(values):
    158     d = OrderedDict()
    159     for i in values:
    160       d[i.name] = i
    161 
    162     return d
    163 
    164   def validate_tree(self):
    165     """
    166     Sanity check the tree recursively, ensuring for a node n, all children's
    167     parents are also n.
    168 
    169     Returns:
    170       True if validation succeeds, False otherwise.
    171     """
    172     succ = True
    173     children = self._get_children()
    174     if children is None:
    175       return True
    176 
    177     for child in self._get_children():
    178       if child.parent != self:
    179         print >> sys.stderr, ("ERROR: Node '%s' doesn't match the parent" +    \
    180                              "(expected: %s, actual %s)")                      \
    181                              %(child, self, child.parent)
    182         succ = False
    183 
    184       succ = child.validate_tree() and succ
    185 
    186     return succ
    187 
    188   def __str__(self):
    189     return "<%s name='%s'>" %(self.__class__, self.name)
    190 
    191 class Metadata(Node):
    192   """
    193   A node corresponding to a <metadata> entry.
    194 
    195   Attributes (Read-Only):
    196     parent: An edge to the parent Node. This is always None for Metadata.
    197     outer_namespaces: A sequence of immediate OuterNamespace children.
    198     tags: A sequence of all Tag instances available in the graph.
    199     types: An iterable of all Typedef instances available in the graph.
    200   """
    201 
    202   def __init__(self):
    203     """
    204     Initialize with no children. Use insert_* functions and then
    205     construct_graph() to build up the Metadata from some source.
    206     """
    207 # Private
    208     self._entries = []
    209     # kind => { name => entry }
    210     self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} }
    211     self._entries_ordered = [] # list of ordered Entry/Clone instances
    212     self._clones = []
    213 
    214 # Public (Read Only)
    215     self._name = None
    216     self._parent = None
    217     self._outer_namespaces = None
    218     self._tags = []
    219     self._types = []
    220 
    221   @property
    222   def outer_namespaces(self):
    223     if self._outer_namespaces is None:
    224       return None
    225     else:
    226       return (i for i in self._outer_namespaces)
    227 
    228   @property
    229   def tags(self):
    230     return (i for i in self._tags)
    231 
    232   @property
    233   def types(self):
    234     return (i for i in self._types)
    235 
    236   def _get_properties(self):
    237 
    238     for i in self._entries:
    239       yield i
    240 
    241     for i in self._clones:
    242       yield i
    243 
    244   def insert_tag(self, tag, description=""):
    245     """
    246     Insert a tag into the metadata.
    247 
    248     Args:
    249       tag: A string identifier for a tag.
    250       description: A string description for a tag.
    251 
    252     Example:
    253       metadata.insert_tag("BC", "Backwards Compatibility for old API")
    254 
    255     Remarks:
    256       Subsequent calls to insert_tag with the same tag are safe (they will
    257       be ignored).
    258     """
    259     tag_ids = [tg.name for tg in self.tags if tg.name == tag]
    260     if not tag_ids:
    261       self._tags.append(Tag(tag, self, description))
    262 
    263   def insert_type(self, type_name, type_selector="typedef", **kwargs):
    264     """
    265     Insert a type into the metadata.
    266 
    267     Args:
    268       type_name: A type's name
    269       type_selector: The selector for the type, e.g. 'typedef'
    270 
    271     Args (if type_selector == 'typedef'):
    272       languages: A map of 'language name' -> 'fully qualified class path'
    273 
    274     Example:
    275       metadata.insert_type('rectangle', 'typedef',
    276                            { 'java': 'android.graphics.Rect' })
    277 
    278     Remarks:
    279       Subsequent calls to insert_type with the same type name are safe (they
    280       will be ignored)
    281     """
    282 
    283     if type_selector != 'typedef':
    284       raise ValueError("Unsupported type_selector given " + type_selector)
    285 
    286     type_names = [tp.name for tp in self.types if tp.name == tp]
    287     if not type_names:
    288       self._types.append(Typedef(type_name, self, kwargs.get('languages')))
    289 
    290   def insert_entry(self, entry):
    291     """
    292     Insert an entry into the metadata.
    293 
    294     Args:
    295       entry: A key-value dictionary describing an entry. Refer to
    296              Entry#__init__ for the keys required/optional.
    297 
    298     Remarks:
    299       Subsequent calls to insert_entry with the same entry+kind name are safe
    300       (they will be ignored).
    301     """
    302     e = Entry(**entry)
    303     self._entries.append(e)
    304     self._entry_map[e.kind][e.name] = e
    305     self._entries_ordered.append(e)
    306 
    307   def insert_clone(self, clone):
    308     """
    309     Insert a clone into the metadata.
    310 
    311     Args:
    312       clone: A key-value dictionary describing a clone. Refer to
    313             Clone#__init__ for the keys required/optional.
    314 
    315     Remarks:
    316       Subsequent calls to insert_clone with the same clone+kind name are safe
    317       (they will be ignored). Also the target entry need not be inserted
    318       ahead of the clone entry.
    319     """
    320     # figure out corresponding entry later. allow clone insert, entry insert
    321     entry = None
    322     c = Clone(entry, **clone)
    323     self._entry_map[c.kind][c.name] = c
    324     self._clones.append(c)
    325     self._entries_ordered.append(c)
    326 
    327   def prune_clones(self):
    328     """
    329     Remove all clones that don't point to an existing entry.
    330 
    331     Remarks:
    332       This should be called after all insert_entry/insert_clone calls have
    333       finished.
    334     """
    335     remove_list = []
    336     for p in self._clones:
    337       if p.entry is None:
    338         remove_list.append(p)
    339 
    340     for p in remove_list:
    341 
    342       # remove from parent's entries list
    343       if p.parent is not None:
    344         p.parent._entries.remove(p)
    345       # remove from parents' _leafs list
    346       for ancestor in p.find_parents(lambda x: not isinstance(x, Metadata)):
    347         ancestor._leafs.remove(p)
    348 
    349       # remove from global list
    350       self._clones.remove(p)
    351       self._entry_map[p.kind].pop(p.name)
    352       self._entries_ordered.remove(p)
    353 
    354   def is_entry_this_kind(self, entry, kind):
    355     """
    356     Check if input entry if of input kind
    357 
    358     Args:
    359       entry: an Entry object
    360       kind: a string. Possible values are "static", "dynamic", "controls"
    361 
    362     Returns:
    363       A boolean indicating whether input entry is of input kind.
    364     """
    365     if kind not in ("static", "dynamic", "controls"):
    366       assert(False), "Unknown kind value " + kind
    367 
    368     return entry.name in self._entry_map[kind]
    369 
    370   # After all entries/clones are inserted,
    371   # invoke this to generate the parent/child node graph all these objects
    372   def construct_graph(self):
    373     """
    374     Generate the graph recursively, after which all Entry nodes will be
    375     accessible recursively by crawling through the outer_namespaces sequence.
    376 
    377     Remarks:
    378       This is safe to be called multiple times at any time. It should be done at
    379       least once or there will be no graph.
    380     """
    381     self.validate_tree()
    382     self._construct_tags()
    383     self.validate_tree()
    384     self._construct_types()
    385     self.validate_tree()
    386     self._construct_clones()
    387     self.validate_tree()
    388     self._construct_outer_namespaces()
    389     self.validate_tree()
    390 
    391   def _construct_tags(self):
    392     tag_dict = self._dictionary_by_name(self.tags)
    393     for p in self._get_properties():
    394       p._tags = []
    395       for tag_id in p._tag_ids:
    396         tag = tag_dict.get(tag_id)
    397 
    398         if tag not in p._tags:
    399           p._tags.append(tag)
    400 
    401         if p not in tag.entries:
    402           tag._entries.append(p)
    403 
    404   def _construct_types(self):
    405     type_dict = self._dictionary_by_name(self.types)
    406     for p in self._get_properties():
    407       if p._type_name:
    408         type_node = type_dict.get(p._type_name)
    409         p._typedef = type_node
    410 
    411         if p not in type_node.entries:
    412           type_node._entries.append(p)
    413 
    414   def _construct_clones(self):
    415     for p in self._clones:
    416       target_kind = p.target_kind
    417       target_entry = self._entry_map[target_kind].get(p.name)
    418       p._entry = target_entry
    419       if (p.hal_major_version == 0):
    420         p._hal_major_version = target_entry._hal_major_version
    421         p._hal_minor_version = target_entry._hal_minor_version
    422       # should not throw if we pass validation
    423       # but can happen when importing obsolete CSV entries
    424       if target_entry is None:
    425         print >> sys.stderr, ("WARNING: Clone entry '%s' target kind '%s'" +   \
    426                               " has no corresponding entry")                   \
    427                              %(p.name, p.target_kind)
    428 
    429   def _construct_outer_namespaces(self):
    430 
    431     if self._outer_namespaces is None: #the first time this runs
    432       self._outer_namespaces = []
    433 
    434     root = self._dictionary_by_name(self._outer_namespaces)
    435     for ons_name, ons in root.iteritems():
    436       ons._leafs = []
    437 
    438     for p in self._entries_ordered:
    439       ons_name = p.get_outer_namespace()
    440       ons = root.get(ons_name, OuterNamespace(ons_name, self))
    441       root[ons_name] = ons
    442 
    443       if p not in ons._leafs:
    444         ons._leafs.append(p)
    445 
    446     for ons_name, ons in root.iteritems():
    447 
    448       ons.validate_tree()
    449 
    450       self._construct_sections(ons)
    451 
    452       if ons not in self._outer_namespaces:
    453         self._outer_namespaces.append(ons)
    454 
    455       ons.validate_tree()
    456 
    457   def _construct_sections(self, outer_namespace):
    458 
    459     sections_dict = self._dictionary_by_name(outer_namespace.sections)
    460     for sec_name, sec in sections_dict.iteritems():
    461       sec._leafs = []
    462       sec.validate_tree()
    463 
    464     for p in outer_namespace._leafs:
    465       does_exist = sections_dict.get(p.get_section())
    466 
    467       sec = sections_dict.get(p.get_section(), \
    468           Section(p.get_section(), outer_namespace))
    469       sections_dict[p.get_section()] = sec
    470 
    471       sec.validate_tree()
    472 
    473       if p not in sec._leafs:
    474         sec._leafs.append(p)
    475 
    476     for sec_name, sec in sections_dict.iteritems():
    477 
    478       if not sec.validate_tree():
    479         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
    480                              "construct_sections (start), with section = '%s'")\
    481                              %(sec)
    482 
    483       self._construct_kinds(sec)
    484 
    485       if sec not in outer_namespace.sections:
    486         outer_namespace._sections.append(sec)
    487 
    488       if not sec.validate_tree():
    489         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
    490                               "construct_sections (end), with section = '%s'") \
    491                              %(sec)
    492 
    493   # 'controls', 'static' 'dynamic'. etc
    494   def _construct_kinds(self, section):
    495     for kind in section.kinds:
    496       kind._leafs = []
    497       section.validate_tree()
    498 
    499     group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind)
    500     leaf_it = ((k, g) for k, g in group_entry_by_kind)
    501 
    502     # allow multiple kinds with the same name. merge if adjacent
    503     # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic
    504     # this helps maintain ABI compatibility when adding an entry in a new kind
    505     for idx, (kind_name, entry_it) in enumerate(leaf_it):
    506       if idx >= len(section._kinds):
    507         kind = Kind(kind_name, section)
    508         section._kinds.append(kind)
    509         section.validate_tree()
    510 
    511       kind = section._kinds[idx]
    512 
    513       for p in entry_it:
    514         if p not in kind._leafs:
    515           kind._leafs.append(p)
    516 
    517     for kind in section._kinds:
    518       kind.validate_tree()
    519       self._construct_inner_namespaces(kind)
    520       kind.validate_tree()
    521       self._construct_entries(kind)
    522       kind.validate_tree()
    523 
    524       if not section.validate_tree():
    525         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
    526                              "construct_kinds, with kind = '%s'") %(kind)
    527 
    528       if not kind.validate_tree():
    529         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
    530                               "construct_kinds, with kind = '%s'") %(kind)
    531 
    532   def _construct_inner_namespaces(self, parent, depth=0):
    533     #parent is InnerNamespace or Kind
    534     ins_dict = self._dictionary_by_name(parent.namespaces)
    535     for name, ins in ins_dict.iteritems():
    536       ins._leafs = []
    537 
    538     for p in parent._leafs:
    539       ins_list = p.get_inner_namespace_list()
    540 
    541       if len(ins_list) > depth:
    542         ins_str = ins_list[depth]
    543         ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent))
    544         ins_dict[ins_str] = ins
    545 
    546         if p not in ins._leafs:
    547           ins._leafs.append(p)
    548 
    549     for name, ins in ins_dict.iteritems():
    550       ins.validate_tree()
    551       # construct children INS
    552       self._construct_inner_namespaces(ins, depth + 1)
    553       ins.validate_tree()
    554       # construct children entries
    555       self._construct_entries(ins, depth + 1)
    556 
    557       if ins not in parent.namespaces:
    558         parent._namespaces.append(ins)
    559 
    560       if not ins.validate_tree():
    561         print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
    562                               "construct_inner_namespaces, with ins = '%s'")   \
    563                              %(ins)
    564 
    565   # doesnt construct the entries, so much as links them
    566   def _construct_entries(self, parent, depth=0):
    567     #parent is InnerNamespace or Kind
    568     entry_dict = self._dictionary_by_name(parent.entries)
    569     for p in parent._leafs:
    570       ins_list = p.get_inner_namespace_list()
    571 
    572       if len(ins_list) == depth:
    573         entry = entry_dict.get(p.name, p)
    574         entry_dict[p.name] = entry
    575 
    576     for name, entry in entry_dict.iteritems():
    577 
    578       old_parent = entry.parent
    579       entry._parent = parent
    580 
    581       if entry not in parent.entries:
    582         parent._entries.append(entry)
    583 
    584       if old_parent is not None and old_parent != parent:
    585         print >> sys.stderr, ("ERROR: Parent changed from '%s' to '%s' for " + \
    586                               "entry '%s'")                                    \
    587                              %(old_parent.name, parent.name, entry.name)
    588 
    589   def _get_children(self):
    590     if self.outer_namespaces is not None:
    591       for i in self.outer_namespaces:
    592         yield i
    593 
    594     if self.tags is not None:
    595       for i in self.tags:
    596         yield i
    597 
    598 class Tag(Node):
    599   """
    600   A tag Node corresponding to a top-level <tag> element.
    601 
    602   Attributes (Read-Only):
    603     name: alias for id
    604     id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC'
    605     description: The description of the tag, the contents of the <tag> element.
    606     parent: An edge to the parent, which is always the Metadata root node.
    607     entries: A sequence of edges to entries/clones that are using this Tag.
    608   """
    609   def __init__(self, name, parent, description=""):
    610     self._name        = name  # 'id' attribute in XML
    611     self._id          = name
    612     self._description = description
    613     self._parent      = parent
    614 
    615     # all entries that have this tag, including clones
    616     self._entries     = []  # filled in by Metadata#construct_tags
    617 
    618   @property
    619   def id(self):
    620     return self._id
    621 
    622   @property
    623   def description(self):
    624     return self._description
    625 
    626   @property
    627   def entries(self):
    628     return (i for i in self._entries)
    629 
    630   def _get_children(self):
    631     return None
    632 
    633 class Typedef(Node):
    634   """
    635   A typedef Node corresponding to a <typedef> element under a top-level <types>.
    636 
    637   Attributes (Read-Only):
    638     name: The name of this typedef as a string.
    639     languages: A dictionary of 'language name' -> 'fully qualified class'.
    640     parent: An edge to the parent, which is always the Metadata root node.
    641     entries: An iterable over all entries which reference this typedef.
    642   """
    643   def __init__(self, name, parent, languages=None):
    644     self._name        = name
    645     self._parent      = parent
    646 
    647     # all entries that have this typedef
    648     self._entries     = []  # filled in by Metadata#construct_types
    649 
    650     self._languages   = languages or {}
    651 
    652   @property
    653   def languages(self):
    654     return self._languages
    655 
    656   @property
    657   def entries(self):
    658     return (i for i in self._entries)
    659 
    660   def _get_children(self):
    661     return None
    662 
    663 class OuterNamespace(Node):
    664   """
    665   A node corresponding to a <namespace> element under <metadata>
    666 
    667   Attributes (Read-Only):
    668     name: The name attribute of the <namespace name="foo"> element.
    669     parent: An edge to the parent, which is always the Metadata root node.
    670     sections: A sequence of Section children.
    671   """
    672   def __init__(self, name, parent, sections=[]):
    673     self._name = name
    674     self._parent = parent # MetadataSet
    675     self._sections = sections[:]
    676     self._leafs = []
    677 
    678     self._children = self._sections
    679 
    680   @property
    681   def sections(self):
    682     return (i for i in self._sections)
    683 
    684 class Section(Node):
    685   """
    686   A node corresponding to a <section> element under <namespace>
    687 
    688   Attributes (Read-Only):
    689     name: The name attribute of the <section name="foo"> element.
    690     parent: An edge to the parent, which is always an OuterNamespace instance.
    691     description: A string description of the section, or None.
    692     kinds: A sequence of Kind children.
    693     merged_kinds: A sequence of virtual Kind children,
    694                   with each Kind's children merged by the kind.name
    695     hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
    696   """
    697   def __init__(self, name, parent, description=None, kinds=[]):
    698     self._name = name
    699     self._parent = parent
    700     self._description = description
    701     self._kinds = kinds[:]
    702 
    703     self._leafs = []
    704 
    705   @property
    706   def description(self):
    707     return self._description
    708 
    709   @property
    710   def kinds(self):
    711     return (i for i in self._kinds)
    712 
    713   @property
    714   def hal_versions(self):
    715     hal_versions = set()
    716     for i in self._kinds:
    717       for entry in i.entries:
    718         hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
    719       for namespace in i.namespaces:
    720         hal_versions.update(namespace.hal_versions)
    721     return hal_versions
    722 
    723   def sort_children(self):
    724     self.validate_tree()
    725     # order is always controls,static,dynamic
    726     find_child = lambda x: [i for i in self._get_children() if i.name == x]
    727     new_lst = find_child('controls') \
    728             + find_child('static')   \
    729             + find_child('dynamic')
    730     self._kinds = new_lst
    731     self.validate_tree()
    732 
    733   def _get_children(self):
    734     return (i for i in self.kinds)
    735 
    736   @property
    737   def merged_kinds(self):
    738 
    739     def aggregate_by_name(acc, el):
    740       existing = [i for i in acc if i.name == el.name]
    741       if existing:
    742         k = existing[0]
    743       else:
    744         k = Kind(el.name, el.parent)
    745         acc.append(k)
    746 
    747       k._namespaces.extend(el._namespaces)
    748       k._entries.extend(el._entries)
    749 
    750       return acc
    751 
    752     new_kinds_lst = reduce(aggregate_by_name, self.kinds, [])
    753 
    754     for k in new_kinds_lst:
    755       yield k
    756 
    757   def combine_kinds_into_single_node(self):
    758     r"""
    759     Combines the section's Kinds into a single node.
    760 
    761     Combines all the children (kinds) of this section into a single
    762     virtual Kind node.
    763 
    764     Returns:
    765       A new Kind node that collapses all Kind siblings into one, combining
    766       all their children together.
    767 
    768       For example, given self.kinds == [ x, y ]
    769 
    770         x  y               z
    771       / |  | \    -->   / | | \
    772       a b  c d          a b c d
    773 
    774       a new instance z is returned in this example.
    775 
    776     Remarks:
    777       The children of the kinds are the same references as before, that is
    778       their parents will point to the old parents and not to the new parent.
    779     """
    780     combined = Kind(name="combined", parent=self)
    781 
    782     for k in self._get_children():
    783       combined._namespaces.extend(k.namespaces)
    784       combined._entries.extend(k.entries)
    785 
    786     return combined
    787 
    788 class Kind(Node):
    789   """
    790   A node corresponding to one of: <static>,<dynamic>,<controls> under a
    791   <section> element.
    792 
    793   Attributes (Read-Only):
    794     name: A string which is one of 'static', 'dynamic, or 'controls'.
    795     parent: An edge to the parent, which is always a Section  instance.
    796     namespaces: A sequence of InnerNamespace children.
    797     entries: A sequence of Entry/Clone children.
    798     merged_entries: A sequence of MergedEntry virtual nodes from entries
    799   """
    800   def __init__(self, name, parent):
    801     self._name = name
    802     self._parent = parent
    803     self._namespaces = []
    804     self._entries = []
    805 
    806     self._leafs = []
    807 
    808   @property
    809   def namespaces(self):
    810     return self._namespaces
    811 
    812   @property
    813   def entries(self):
    814     return self._entries
    815 
    816   @property
    817   def merged_entries(self):
    818     for i in self.entries:
    819       yield i.merge()
    820 
    821   def sort_children(self):
    822     self._namespaces.sort(key=self._get_name())
    823     self._entries.sort(key=self._get_name())
    824 
    825   def _get_children(self):
    826     for i in self.namespaces:
    827       yield i
    828     for i in self.entries:
    829       yield i
    830 
    831   def combine_children_by_name(self):
    832     r"""
    833     Combine multiple children with the same name into a single node.
    834 
    835     Returns:
    836       A new Kind where all of the children with the same name were combined.
    837 
    838       For example:
    839 
    840       Given a Kind k:
    841 
    842               k
    843             / | \
    844             a b c
    845             | | |
    846             d e f
    847 
    848       a.name == "foo"
    849       b.name == "foo"
    850       c.name == "bar"
    851 
    852       The returned Kind will look like this:
    853 
    854              k'
    855             /  \
    856             a' c'
    857           / |  |
    858           d e  f
    859 
    860     Remarks:
    861       This operation is not recursive. To combine the grandchildren and other
    862       ancestors, call this method on the ancestor nodes.
    863     """
    864     return Kind._combine_children_by_name(self, new_type=type(self))
    865 
    866   # new_type is either Kind or InnerNamespace
    867   @staticmethod
    868   def _combine_children_by_name(self, new_type):
    869     new_ins_dict = OrderedDict()
    870     new_ent_dict = OrderedDict()
    871 
    872     for ins in self.namespaces:
    873       new_ins = new_ins_dict.setdefault(ins.name,
    874                                         InnerNamespace(ins.name, parent=self))
    875       new_ins._namespaces.extend(ins.namespaces)
    876       new_ins._entries.extend(ins.entries)
    877 
    878     for ent in self.entries:
    879       new_ent = new_ent_dict.setdefault(ent.name,
    880                                         ent.merge())
    881 
    882     kind = new_type(self.name, self.parent)
    883     kind._namespaces = new_ins_dict.values()
    884     kind._entries = new_ent_dict.values()
    885 
    886     return kind
    887 
    888 class InnerNamespace(Node):
    889   """
    890   A node corresponding to a <namespace> which is an ancestor of a Kind.
    891   These namespaces may have other namespaces recursively, or entries as leafs.
    892 
    893   Attributes (Read-Only):
    894     name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
    895     parent: An edge to the parent, which is an InnerNamespace or a Kind.
    896     namespaces: A sequence of InnerNamespace children.
    897     entries: A sequence of Entry/Clone children.
    898     merged_entries: A sequence of MergedEntry virtual nodes from entries
    899     hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
    900   """
    901   def __init__(self, name, parent):
    902     self._name        = name
    903     self._parent      = parent
    904     self._namespaces  = []
    905     self._entries     = []
    906     self._leafs       = []
    907 
    908   @property
    909   def namespaces(self):
    910     return self._namespaces
    911 
    912   @property
    913   def entries(self):
    914     return self._entries
    915 
    916   @property
    917   def hal_versions(self):
    918     hal_versions = set()
    919     for entry in self.entries:
    920       hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
    921     for namespace in self.namespaces:
    922       hal_versions.update(namespace.hal_versions)
    923     return hal_versions
    924 
    925   @property
    926   def merged_entries(self):
    927     for i in self.entries:
    928       yield i.merge()
    929 
    930   def sort_children(self):
    931     self._namespaces.sort(key=self._get_name())
    932     self._entries.sort(key=self._get_name())
    933 
    934   def _get_children(self):
    935     for i in self.namespaces:
    936       yield i
    937     for i in self.entries:
    938       yield i
    939 
    940   def combine_children_by_name(self):
    941     r"""
    942     Combine multiple children with the same name into a single node.
    943 
    944     Returns:
    945       A new InnerNamespace where all of the children with the same name were
    946       combined.
    947 
    948       For example:
    949 
    950       Given an InnerNamespace i:
    951 
    952               i
    953             / | \
    954             a b c
    955             | | |
    956             d e f
    957 
    958       a.name == "foo"
    959       b.name == "foo"
    960       c.name == "bar"
    961 
    962       The returned InnerNamespace will look like this:
    963 
    964              i'
    965             /  \
    966             a' c'
    967           / |  |
    968           d e  f
    969 
    970     Remarks:
    971       This operation is not recursive. To combine the grandchildren and other
    972       ancestors, call this method on the ancestor nodes.
    973     """
    974     return Kind._combine_children_by_name(self, new_type=type(self))
    975 
    976 class EnumValue(Node):
    977   """
    978   A class corresponding to a <value> element within an <enum> within an <entry>.
    979 
    980   Attributes (Read-Only):
    981     name: A string,                 e.g. 'ON' or 'OFF'
    982     id: An optional numeric string, e.g. '0' or '0xFF'
    983     deprecated: A boolean, True if the enum should be deprecated.
    984     optional: A boolean
    985     hidden: A boolean, True if the enum should be hidden.
    986     ndk_hidden: A boolean, True if the enum should be hidden in NDK
    987     notes: A string describing the notes, or None.
    988     sdk_notes: A string describing extra notes for public SDK only
    989     ndk_notes: A string describing extra notes for public NDK only
    990     parent: An edge to the parent, always an Enum instance.
    991     hal_major_version: The major HIDL HAL version this value was first added in
    992     hal_minor_version: The minor HIDL HAL version this value was first added in
    993   """
    994   def __init__(self, name, parent,
    995       id=None, deprecated=False, optional=False, hidden=False, notes=None, sdk_notes=None, ndk_notes=None, ndk_hidden=False, hal_version='3.2'):
    996     self._name = name                    # str, e.g. 'ON' or 'OFF'
    997     self._id = id                        # int, e.g. '0'
    998     self._deprecated = deprecated        # bool
    999     self._optional = optional            # bool
   1000     self._hidden = hidden                # bool
   1001     self._ndk_hidden = ndk_hidden        # bool
   1002     self._notes = notes                  # None or str
   1003     self._sdk_notes = sdk_notes          # None or str
   1004     self._ndk_notes = ndk_notes          # None or str
   1005     self._parent = parent
   1006     if hal_version is None:
   1007       if parent is not None and parent.parent is not None:
   1008         self._hal_major_version = parent.parent.hal_major_version
   1009         self._hal_minor_version = parent.parent.hal_minor_version
   1010       else:
   1011         self._hal_major_version = 3
   1012         self._hal_minor_version = 2
   1013     else:
   1014       self._hal_major_version = int(hal_version.partition('.')[0])
   1015       self._hal_minor_version = int(hal_version.partition('.')[2])
   1016 
   1017   @property
   1018   def id(self):
   1019     return self._id
   1020 
   1021   @property
   1022   def deprecated(self):
   1023     return self._deprecated
   1024 
   1025   @property
   1026   def optional(self):
   1027     return self._optional
   1028 
   1029   @property
   1030   def hidden(self):
   1031     return self._hidden
   1032 
   1033   @property
   1034   def ndk_hidden(self):
   1035     return self._ndk_hidden
   1036 
   1037   @property
   1038   def notes(self):
   1039     return self._notes
   1040 
   1041   @property
   1042   def sdk_notes(self):
   1043     return self._sdk_notes
   1044 
   1045   @property
   1046   def ndk_notes(self):
   1047     return self._ndk_notes
   1048 
   1049   @property
   1050   def hal_major_version(self):
   1051     return self._hal_major_version
   1052 
   1053   @property
   1054   def hal_minor_version(self):
   1055     return self._hal_minor_version
   1056 
   1057   def _get_children(self):
   1058     return None
   1059 
   1060 class Enum(Node):
   1061   """
   1062   A class corresponding to an <enum> element within an <entry>.
   1063 
   1064   Attributes (Read-Only):
   1065     parent: An edge to the parent, always an Entry instance.
   1066     values: A sequence of EnumValue children.
   1067     has_values_with_id: A boolean representing if any of the children have a
   1068         non-empty id property.
   1069   """
   1070   def __init__(self, parent, values, ids={}, deprecateds=[],
   1071       optionals=[], hiddens=[], notes={}, sdk_notes={}, ndk_notes={}, ndk_hiddens=[], hal_versions={}):
   1072     self._parent = parent
   1073     self._name = None
   1074     self._values =                                                             \
   1075       [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens,  \
   1076                   notes.get(val), sdk_notes.get(val), ndk_notes.get(val), val in ndk_hiddens, hal_versions.get(val))     \
   1077         for val in values ]
   1078 
   1079   @property
   1080   def values(self):
   1081     return (i for i in self._values)
   1082 
   1083   @property
   1084   def has_values_with_id(self):
   1085     return bool(any(i for i in self.values if i.id))
   1086 
   1087   def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
   1088     return bool(any(i for i in self.values if i.hal_major_version == hal_major_version and i.hal_minor_version == hal_minor_version))
   1089 
   1090   def _get_children(self):
   1091     return (i for i in self._values)
   1092 
   1093 class Entry(Node):
   1094   """
   1095   A node corresponding to an <entry> element.
   1096 
   1097   Attributes (Read-Only):
   1098     parent: An edge to the parent node, which is an InnerNamespace or Kind.
   1099     name: The fully qualified name string, e.g. 'android.shading.mode'
   1100     name_short: The name attribute from <entry name="mode">, e.g. mode
   1101     type: The type attribute from <entry type="bar">
   1102     kind: A string ('static', 'dynamic', 'controls') corresponding to the
   1103           ancestor Kind#name
   1104     container: The container attribute from <entry container="array">, or None.
   1105     container_sizes: A sequence of size strings or None if container is None.
   1106     enum: An Enum instance if the enum attribute is true, None otherwise.
   1107     visibility: The visibility of this entry ('system', 'hidden', 'public')
   1108                 across the system. System entries are only visible in native code
   1109                 headers. Hidden entries are marked @hide in managed code, while
   1110                 public entries are visible in the Android SDK.
   1111     applied_visibility: As visibility, but always valid, defaulting to 'system'
   1112                         if no visibility is given for an entry.
   1113     applied_ndk_visible: Always valid. Default is 'false'.
   1114                          Set to 'true' when the visibility implied entry is visible
   1115                          in NDK.
   1116     synthetic: The C-level visibility of this entry ('false', 'true').
   1117                Synthetic entries will not be generated into the native metadata
   1118                list of entries (in C code). In general a synthetic entry is
   1119                glued together at the Java layer from multiple visibiltity=hidden
   1120                entries.
   1121     hwlevel: The lowest hardware level at which the entry is guaranteed
   1122              to be supported by the camera device. All devices with higher
   1123              hwlevels will also include this entry. None means that the
   1124              entry is optional on any hardware level.
   1125     deprecated: Marks an entry as @Deprecated in the Java layer; if within an
   1126                unreleased version this needs to be removed altogether. If applied
   1127                to an entry from an older release, then this means the entry
   1128                should be ignored by newer code.
   1129     optional: a bool representing the optional attribute, which denotes the entry
   1130               is required for hardware level full devices, but optional for other
   1131               hardware levels.  None if not present.
   1132     applied_optional: As optional but always valid, defaulting to False if no
   1133                       optional attribute is present.
   1134     tuple_values: A sequence of strings describing the tuple values,
   1135                   None if container is not 'tuple'.
   1136     description: A string description, or None.
   1137     deprecation_description: A string describing the reason for deprecation. Must be present
   1138                  if deprecated is true, otherwise may be None.
   1139     range: A string range, or None.
   1140     units: A string units, or None.
   1141     tags: A sequence of Tag nodes associated with this Entry.
   1142     type_notes: A string describing notes for the type, or None.
   1143     typedef: A Typedef associated with this Entry, or None.
   1144 
   1145   Remarks:
   1146     Subclass Clone can be used interchangeable with an Entry,
   1147     for when we don't care about the underlying type.
   1148 
   1149     parent and tags edges are invalid until after Metadata#construct_graph
   1150     has been invoked.
   1151   """
   1152   def __init__(self, **kwargs):
   1153     """
   1154     Instantiate a new Entry node.
   1155 
   1156     Args:
   1157       name: A string with the fully qualified name, e.g. 'android.shading.mode'
   1158       type: A string describing the type, e.g. 'int32'
   1159       kind: A string describing the kind, e.g. 'static'
   1160       hal_version: A string for the initial HIDL HAL metadata version this entry
   1161                    was added in
   1162 
   1163     Args (if container):
   1164       container: A string describing the container, e.g. 'array' or 'tuple'
   1165       container_sizes: A list of string sizes if a container, or None otherwise
   1166 
   1167     Args (if container is 'tuple'):
   1168       tuple_values: A list of tuple values, e.g. ['width', 'height']
   1169 
   1170     Args (if the 'enum' attribute is true):
   1171       enum: A boolean, True if this is an enum, False otherwise
   1172       enum_values: A list of value strings, e.g. ['ON', 'OFF']
   1173       enum_optionals: A list of optional enum values, e.g. ['OFF']
   1174       enum_notes: A dictionary of value->notes strings.
   1175       enum_ids: A dictionary of value->id strings.
   1176       enum_hal_versions: A dictionary of value->hal version strings
   1177 
   1178     Args (if the 'deprecated' attribute is true):
   1179       deprecation_description: A string explaining the deprecation, to be added
   1180                                to the Java-layer @deprecated tag
   1181 
   1182     Args (optional):
   1183       description: A string with a description of the entry.
   1184       range: A string with the range of the values of the entry, e.g. '>= 0'
   1185       units: A string with the units of the values, e.g. 'inches'
   1186       details: A string with the detailed documentation for the entry
   1187       hal_details: A string with the HAL implementation details for the entry
   1188       ndk_details: A string with the extra NDK API documentation for the entry=
   1189       tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
   1190       type_notes: A string with the notes for the type
   1191       visibility: A string describing the visibility, eg 'system', 'hidden',
   1192                   'public'
   1193       synthetic: A bool to mark whether this entry is visible only at the Java
   1194                  layer (True), or at both layers (False = default).
   1195       hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
   1196       deprecated: A bool to mark whether this is @Deprecated at the Java layer
   1197                  (default = False).
   1198       optional: A bool to mark whether optional for non-full hardware devices
   1199       typedef: A string corresponding to a typedef's name attribute.
   1200     """
   1201 
   1202     if kwargs.get('type') is None:
   1203       print >> sys.stderr, "ERROR: Missing type for entry '%s' kind  '%s'"     \
   1204       %(kwargs.get('name'), kwargs.get('kind'))
   1205 
   1206     # Attributes are Read-Only, but edges may be mutated by
   1207     # Metadata, particularly during construct_graph
   1208 
   1209     self._name = kwargs['name']
   1210     self._type = kwargs['type']
   1211     self._kind = kwargs['kind'] # static, dynamic, or controls
   1212 
   1213     self._init_common(**kwargs)
   1214 
   1215   @property
   1216   def type(self):
   1217     return self._type
   1218 
   1219   @property
   1220   def kind(self):
   1221     return self._kind
   1222 
   1223   @property
   1224   def hal_major_version(self):
   1225     return self._hal_major_version
   1226 
   1227   @property
   1228   def hal_minor_version(self):
   1229     return self._hal_minor_version
   1230 
   1231   @property
   1232   def visibility(self):
   1233     return self._visibility
   1234 
   1235   @property
   1236   def applied_visibility(self):
   1237     return self._visibility or 'system'
   1238 
   1239   @property
   1240   def applied_ndk_visible(self):
   1241     if self._visibility in ("public", "ndk_public"):
   1242       return "true"
   1243     return "false"
   1244 
   1245   @property
   1246   def synthetic(self):
   1247     return self._synthetic
   1248 
   1249   @property
   1250   def hwlevel(self):
   1251     return self._hwlevel
   1252 
   1253   @property
   1254   def deprecated(self):
   1255     return self._deprecated
   1256 
   1257   @property
   1258   def deprecation_description(self):
   1259     return self._deprecation_description
   1260 
   1261   # TODO: optional should just return hwlevel is None
   1262   @property
   1263   def optional(self):
   1264     return self._optional
   1265 
   1266   @property
   1267   def applied_optional(self):
   1268     return self._optional or False
   1269 
   1270   @property
   1271   def name_short(self):
   1272     return self.get_name_minimal()
   1273 
   1274   @property
   1275   def container(self):
   1276     return self._container
   1277 
   1278   @property
   1279   def container_sizes(self):
   1280     if self._container_sizes is None:
   1281       return None
   1282     else:
   1283       return (i for i in self._container_sizes)
   1284 
   1285   @property
   1286   def tuple_values(self):
   1287     if self._tuple_values is None:
   1288       return None
   1289     else:
   1290       return (i for i in self._tuple_values)
   1291 
   1292   @property
   1293   def description(self):
   1294     return self._description
   1295 
   1296   @property
   1297   def range(self):
   1298     return self._range
   1299 
   1300   @property
   1301   def units(self):
   1302     return self._units
   1303 
   1304   @property
   1305   def details(self):
   1306     return self._details
   1307 
   1308   @property
   1309   def hal_details(self):
   1310     return self._hal_details
   1311 
   1312   @property
   1313   def ndk_details(self):
   1314     return self._ndk_details
   1315 
   1316   @property
   1317   def applied_ndk_details(self):
   1318     return (self._details or "") + (self._ndk_details or "")
   1319 
   1320   @property
   1321   def tags(self):
   1322     if self._tags is None:
   1323       return None
   1324     else:
   1325       return (i for i in self._tags)
   1326 
   1327   @property
   1328   def type_notes(self):
   1329     return self._type_notes
   1330 
   1331   @property
   1332   def typedef(self):
   1333     return self._typedef
   1334 
   1335   @property
   1336   def enum(self):
   1337     return self._enum
   1338 
   1339   def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
   1340     if self._enum is not None:
   1341       return self._enum.has_new_values_added_in_hal_version(hal_major_version,hal_minor_version)
   1342     else:
   1343       return False
   1344 
   1345   def _get_children(self):
   1346     if self.enum:
   1347       yield self.enum
   1348 
   1349   def sort_children(self):
   1350     return None
   1351 
   1352   def is_clone(self):
   1353     """
   1354     Whether or not this is a Clone instance.
   1355 
   1356     Returns:
   1357       False
   1358     """
   1359     return False
   1360 
   1361   def _init_common(self, **kwargs):
   1362 
   1363     self._parent = None # filled in by Metadata::_construct_entries
   1364 
   1365     self._container = kwargs.get('container')
   1366     self._container_sizes = kwargs.get('container_sizes')
   1367 
   1368     hal_version = kwargs.get('hal_version')
   1369     if hal_version is None:
   1370       if self.is_clone():
   1371         self._hal_major_version = 0
   1372         self._hal_minor_version = 0
   1373       else:
   1374         self._hal_major_version = 3
   1375         self._hal_minor_version = 2
   1376     else:
   1377       self._hal_major_version = int(hal_version.partition('.')[0])
   1378       self._hal_minor_version = int(hal_version.partition('.')[2])
   1379 
   1380     # access these via the 'enum' prop
   1381     enum_values = kwargs.get('enum_values')
   1382     enum_deprecateds = kwargs.get('enum_deprecateds')
   1383     enum_optionals = kwargs.get('enum_optionals')
   1384     enum_hiddens = kwargs.get('enum_hiddens')
   1385     enum_ndk_hiddens = kwargs.get('enum_ndk_hiddens')
   1386     enum_notes = kwargs.get('enum_notes')  # { value => notes }
   1387     enum_sdk_notes = kwargs.get('enum_sdk_notes')  # { value => sdk_notes }
   1388     enum_ndk_notes = kwargs.get('enum_ndk_notes')  # { value => ndk_notes }
   1389     enum_ids = kwargs.get('enum_ids')  # { value => notes }
   1390     enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions }
   1391 
   1392     self._tuple_values = kwargs.get('tuple_values')
   1393 
   1394     self._description = kwargs.get('description')
   1395     self._range = kwargs.get('range')
   1396     self._units = kwargs.get('units')
   1397     self._details = kwargs.get('details')
   1398     self._hal_details = kwargs.get('hal_details')
   1399     self._ndk_details = kwargs.get('ndk_details')
   1400 
   1401     self._tag_ids = kwargs.get('tag_ids', [])
   1402     self._tags = None  # Filled in by Metadata::_construct_tags
   1403 
   1404     self._type_notes = kwargs.get('type_notes')
   1405     self._type_name = kwargs.get('type_name')
   1406     self._typedef = None # Filled in by Metadata::_construct_types
   1407 
   1408     if kwargs.get('enum', False):
   1409       self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
   1410                         enum_hiddens, enum_notes, enum_sdk_notes, enum_ndk_notes, enum_ndk_hiddens, enum_hal_versions)
   1411     else:
   1412       self._enum = None
   1413 
   1414     self._visibility = kwargs.get('visibility')
   1415     self._synthetic = kwargs.get('synthetic', False)
   1416     self._hwlevel = kwargs.get('hwlevel')
   1417     self._deprecated = kwargs.get('deprecated', False)
   1418     self._deprecation_description = kwargs.get('deprecation_description')
   1419 
   1420     self._optional = kwargs.get('optional')
   1421     self._ndk_visible = kwargs.get('ndk_visible')
   1422 
   1423     self._property_keys = kwargs
   1424 
   1425   def merge(self):
   1426     """
   1427     Copy the attributes into a new entry, merging it with the target entry
   1428     if it's a clone.
   1429     """
   1430     return MergedEntry(self)
   1431 
   1432   # Helpers for accessing less than the fully qualified name
   1433 
   1434   def get_name_as_list(self):
   1435     """
   1436     Returns the name as a list split by a period.
   1437 
   1438     For example:
   1439       entry.name is 'android.lens.info.shading'
   1440       entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
   1441     """
   1442     return self.name.split(".")
   1443 
   1444   def get_inner_namespace_list(self):
   1445     """
   1446     Returns the inner namespace part of the name as a list
   1447 
   1448     For example:
   1449       entry.name is 'android.lens.info.shading'
   1450       entry.get_inner_namespace_list() == ['info']
   1451     """
   1452     return self.get_name_as_list()[2:-1]
   1453 
   1454   def get_outer_namespace(self):
   1455     """
   1456     Returns the outer namespace as a string.
   1457 
   1458     For example:
   1459       entry.name is 'android.lens.info.shading'
   1460       entry.get_outer_namespace() == 'android'
   1461 
   1462     Remarks:
   1463       Since outer namespaces are non-recursive,
   1464       and each entry has one, this does not need to be a list.
   1465     """
   1466     return self.get_name_as_list()[0]
   1467 
   1468   def get_section(self):
   1469     """
   1470     Returns the section as a string.
   1471 
   1472     For example:
   1473       entry.name is 'android.lens.info.shading'
   1474       entry.get_section() == ''
   1475 
   1476     Remarks:
   1477       Since outer namespaces are non-recursive,
   1478       and each entry has one, this does not need to be a list.
   1479     """
   1480     return self.get_name_as_list()[1]
   1481 
   1482   def get_name_minimal(self):
   1483     """
   1484     Returns only the last component of the fully qualified name as a string.
   1485 
   1486     For example:
   1487       entry.name is 'android.lens.info.shading'
   1488       entry.get_name_minimal() == 'shading'
   1489 
   1490     Remarks:
   1491       entry.name_short it an alias for this
   1492     """
   1493     return self.get_name_as_list()[-1]
   1494 
   1495   def get_path_without_name(self):
   1496     """
   1497     Returns a string path to the entry, with the name component excluded.
   1498 
   1499     For example:
   1500       entry.name is 'android.lens.info.shading'
   1501       entry.get_path_without_name() == 'android.lens.info'
   1502     """
   1503     return ".".join(self.get_name_as_list()[0:-1])
   1504 
   1505 
   1506 class Clone(Entry):
   1507   """
   1508   A Node corresponding to a <clone> element. It has all the attributes of an
   1509   <entry> element (Entry) plus the additions specified below.
   1510 
   1511   Attributes (Read-Only):
   1512     entry: an edge to an Entry object that this targets
   1513     target_kind: A string describing the kind of the target entry.
   1514     name: a string of the name, same as entry.name
   1515     kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
   1516           for the <clone> element.
   1517     type: always None, since a clone cannot override the type.
   1518   """
   1519   def __init__(self, entry=None, **kwargs):
   1520     """
   1521     Instantiate a new Clone node.
   1522 
   1523     Args:
   1524       name: A string with the fully qualified name, e.g. 'android.shading.mode'
   1525       type: A string describing the type, e.g. 'int32'
   1526       kind: A string describing the kind, e.g. 'static'
   1527       target_kind: A string for the kind of the target entry, e.g. 'dynamic'
   1528       hal_version: A string for the initial HIDL HAL metadata version this entry
   1529                    was added in
   1530 
   1531     Args (if container):
   1532       container: A string describing the container, e.g. 'array' or 'tuple'
   1533       container_sizes: A list of string sizes if a container, or None otherwise
   1534 
   1535     Args (if container is 'tuple'):
   1536       tuple_values: A list of tuple values, e.g. ['width', 'height']
   1537 
   1538     Args (if the 'enum' attribute is true):
   1539       enum: A boolean, True if this is an enum, False otherwise
   1540       enum_values: A list of value strings, e.g. ['ON', 'OFF']
   1541       enum_optionals: A list of optional enum values, e.g. ['OFF']
   1542       enum_notes: A dictionary of value->notes strings.
   1543       enum_ids: A dictionary of value->id strings.
   1544 
   1545     Args (optional):
   1546       entry: An edge to the corresponding target Entry.
   1547       description: A string with a description of the entry.
   1548       range: A string with the range of the values of the entry, e.g. '>= 0'
   1549       units: A string with the units of the values, e.g. 'inches'
   1550       details: A string with the detailed documentation for the entry
   1551       hal_details: A string with the HAL implementation details for the entry
   1552       ndk_details: A string with the extra NDK documentation for the entry
   1553       tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
   1554       type_notes: A string with the notes for the type
   1555 
   1556     Remarks:
   1557       Note that type is not specified since it has to be the same as the
   1558       entry.type.
   1559     """
   1560     self._entry = entry  # Entry object
   1561     self._target_kind = kwargs['target_kind']
   1562     self._name = kwargs['name']  # same as entry.name
   1563     self._kind = kwargs['kind']
   1564 
   1565     # illegal to override the type, it should be the same as the entry
   1566     self._type = None
   1567     # the rest of the kwargs are optional
   1568     # can be used to override the regular entry data
   1569     self._init_common(**kwargs)
   1570 
   1571   @property
   1572   def entry(self):
   1573     return self._entry
   1574 
   1575   @property
   1576   def target_kind(self):
   1577     return self._target_kind
   1578 
   1579   def is_clone(self):
   1580     """
   1581     Whether or not this is a Clone instance.
   1582 
   1583     Returns:
   1584       True
   1585     """
   1586     return True
   1587 
   1588 class MergedEntry(Entry):
   1589   """
   1590   A MergedEntry has all the attributes of a Clone and its target Entry merged
   1591   together.
   1592 
   1593   Remarks:
   1594     Useful when we want to 'unfold' a clone into a real entry by copying out
   1595     the target entry data. In this case we don't care about distinguishing
   1596     a clone vs an entry.
   1597   """
   1598   def __init__(self, entry):
   1599     """
   1600     Create a new instance of MergedEntry.
   1601 
   1602     Args:
   1603       entry: An Entry or Clone instance
   1604     """
   1605     props_distinct = ['description', 'units', 'range', 'details',
   1606                       'hal_details', 'ndk_details', 'tags', 'kind',
   1607                       'deprecation_description']
   1608 
   1609     for p in props_distinct:
   1610       p = '_' + p
   1611       if entry.is_clone():
   1612         setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
   1613       else:
   1614         setattr(self, p, getattr(entry, p))
   1615 
   1616     props_common = ['parent', 'name', 'container',
   1617                     'container_sizes', 'enum',
   1618                     'tuple_values',
   1619                     'type',
   1620                     'type_notes',
   1621                     'visibility',
   1622                     'ndk_visible',
   1623                     'synthetic',
   1624                     'hwlevel',
   1625                     'deprecated',
   1626                     'optional',
   1627                     'typedef',
   1628                     'hal_major_version',
   1629                     'hal_minor_version'
   1630                    ]
   1631 
   1632     for p in props_common:
   1633       p = '_' + p
   1634       if entry.is_clone():
   1635         setattr(self, p, getattr(entry.entry, p))
   1636       else:
   1637         setattr(self, p, getattr(entry, p))
   1638