xref: /aosp_15_r20/system/media/camera/docs/metadata_model.py (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
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"""
20A set of classes (models) each closely representing an XML node in the
21metadata_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
38import sys
39import functools
40import itertools
41from collections import OrderedDict
42
43class Node(object):
44  """
45  Base class for most nodes that are part of the Metadata graph.
46
47  Attributes (Read-Only):
48    parent: An edge to a parent Node.
49    name: A string describing the name, usually but not always the 'name'
50          attribute of the corresponding XML node.
51  """
52
53  def __init__(self):
54    self._parent = None
55    self._name = None
56
57  @property
58  def parent(self):
59    return self._parent
60
61  @property
62  def name(self):
63    return self._name
64
65  def find_all(self, pred):
66    """
67    Find all descendants that match the predicate.
68
69    Args:
70      pred: a predicate function that acts as a filter for a Node
71
72    Yields:
73      A sequence of all descendants for which pred(node) is true,
74      in a pre-order visit order.
75    """
76    if pred(self):
77      yield self
78
79    if self._get_children() is None:
80      return
81
82    for i in self._get_children():
83      for j in i.find_all(pred):
84        yield j
85
86  def find_first(self, pred):
87    """
88    Find the first descendant that matches the predicate.
89
90    Args:
91      pred: a predicate function that acts as a filter for a Node
92
93    Returns:
94      The first Node from find_all(pred), or None if there were no results.
95    """
96    for i in self.find_all(pred):
97      return i
98
99    return None
100
101  def find_parent_first(self, pred):
102    """
103    Find the first ancestor that matches the predicate.
104
105    Args:
106      pred: A predicate function that acts as a filter for a Node
107
108    Returns:
109      The first ancestor closest to the node for which pred(node) is true.
110    """
111    for i in self.find_parents(pred):
112      return i
113
114    return None
115
116  def find_parents(self, pred):
117    """
118    Find all ancestors that match the predicate.
119
120    Args:
121      pred: A predicate function that acts as a filter for a Node
122
123    Yields:
124      A sequence of all ancestors (closest to furthest) from the node,
125      where pred(node) is true.
126    """
127    parent = self.parent
128
129    while parent is not None:
130      if pred(parent):
131        yield parent
132      parent = parent.parent
133
134  def sort_children(self):
135    """
136    Sorts the immediate children in-place.
137    """
138    self._sort_by_name(self._children)
139
140  def _sort_by_name(self, what):
141    what.sort(key=lambda x: x.name)
142
143  def _get_name(self):
144    return lambda x: x.name
145
146  # Iterate over all children nodes. None when node doesn't support children.
147  def _get_children(self):
148    return (i for i in self._children)
149
150  def _children_name_map_matching(self, match=lambda x: True):
151    d = {}
152    for i in self._get_children():
153      if match(i):
154        d[i.name] = i
155    return d
156
157  @staticmethod
158  def _dictionary_by_name(values):
159    d = OrderedDict()
160    for i in values:
161      d[i.name] = i
162
163    return d
164
165  def validate_tree(self):
166    """
167    Sanity check the tree recursively, ensuring for a node n, all children's
168    parents are also n.
169
170    Returns:
171      True if validation succeeds, False otherwise.
172    """
173    succ = True
174    children = self._get_children()
175    if children is None:
176      return True
177
178    for child in self._get_children():
179      if child.parent != self:
180        print("ERROR: Node '%s' doesn't match the parent (expected: %s, actual %s)" \
181              % (child, self, child.parent), file=sys.stderr)
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
191class 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("WARNING: Clone entry '%s' target kind '%s' has no corresponding entry" \
426              % (p.name, p.target_kind), file=sys.stderr)
427
428  def _construct_outer_namespaces(self):
429
430    if self._outer_namespaces is None: #the first time this runs
431      self._outer_namespaces = []
432
433    root = self._dictionary_by_name(self._outer_namespaces)
434    for ons_name, ons in root.items():
435      ons._leafs = []
436
437    for p in self._entries_ordered:
438      ons_name = p.get_outer_namespace()
439      ons = root.get(ons_name, OuterNamespace(ons_name, self))
440      root[ons_name] = ons
441
442      if p not in ons._leafs:
443        ons._leafs.append(p)
444
445    for ons_name, ons in root.items():
446
447      ons.validate_tree()
448
449      self._construct_sections(ons)
450
451      if ons not in self._outer_namespaces:
452        self._outer_namespaces.append(ons)
453
454      ons.validate_tree()
455
456  def _construct_sections(self, outer_namespace):
457
458    sections_dict = self._dictionary_by_name(outer_namespace.sections)
459    for sec_name, sec in sections_dict.items():
460      sec._leafs = []
461      sec.validate_tree()
462
463    for p in outer_namespace._leafs:
464      does_exist = sections_dict.get(p.get_section())
465
466      sec = sections_dict.get(p.get_section(), \
467          Section(p.get_section(), outer_namespace))
468      sections_dict[p.get_section()] = sec
469
470      sec.validate_tree()
471
472      if p not in sec._leafs:
473        sec._leafs.append(p)
474
475    for sec_name, sec in sections_dict.items():
476
477      if not sec.validate_tree():
478        print("ERROR: Failed to validate tree in construct_sections (start), with section = '%s'"
479              % (sec), file=sys.stderr)
480
481      self._construct_kinds(sec)
482
483      if sec not in outer_namespace.sections:
484        outer_namespace._sections.append(sec)
485
486      if not sec.validate_tree():
487        print("ERROR: Failed to validate tree in construct_sections (end), with section = '%s'"
488              % (sec), file=sys.stderr)
489
490  # 'controls', 'static' 'dynamic'. etc
491  def _construct_kinds(self, section):
492    for kind in section.kinds:
493      kind._leafs = []
494      section.validate_tree()
495
496    group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind)
497    leaf_it = ((k, g) for k, g in group_entry_by_kind)
498
499    # allow multiple kinds with the same name. merge if adjacent
500    # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic
501    # this helps maintain ABI compatibility when adding an entry in a new kind
502    for idx, (kind_name, entry_it) in enumerate(leaf_it):
503      if idx >= len(section._kinds):
504        kind = Kind(kind_name, section)
505        section._kinds.append(kind)
506        section.validate_tree()
507
508      kind = section._kinds[idx]
509
510      for p in entry_it:
511        if p not in kind._leafs:
512          kind._leafs.append(p)
513
514    for kind in section._kinds:
515      kind.validate_tree()
516      self._construct_inner_namespaces(kind)
517      kind.validate_tree()
518      self._construct_entries(kind)
519      kind.validate_tree()
520
521      if not section.validate_tree():
522        print("ERROR: Failed to validate tree in construct_kinds, with kind = '%s'" % (kind),
523              file=sys.stderr)
524
525      if not kind.validate_tree():
526        print("ERROR: Failed to validate tree in construct_kinds, with kind = '%s'" % (kind),
527              file=sys.stderr)
528
529  def _construct_inner_namespaces(self, parent, depth=0):
530    #parent is InnerNamespace or Kind
531    ins_dict = self._dictionary_by_name(parent.namespaces)
532    for name, ins in ins_dict.items():
533      ins._leafs = []
534
535    for p in parent._leafs:
536      ins_list = p.get_inner_namespace_list()
537
538      if len(ins_list) > depth:
539        ins_str = ins_list[depth]
540        ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent))
541        ins_dict[ins_str] = ins
542
543        if p not in ins._leafs:
544          ins._leafs.append(p)
545
546    for name, ins in ins_dict.items():
547      ins.validate_tree()
548      # construct children INS
549      self._construct_inner_namespaces(ins, depth + 1)
550      ins.validate_tree()
551      # construct children entries
552      self._construct_entries(ins, depth + 1)
553
554      if ins not in parent.namespaces:
555        parent._namespaces.append(ins)
556
557      if not ins.validate_tree():
558        print("ERROR: Failed to validate tree in construct_inner_namespaces, with ins = '%s'"
559              % (ins), file=sys.stderr)
560
561  # doesnt construct the entries, so much as links them
562  def _construct_entries(self, parent, depth=0):
563    #parent is InnerNamespace or Kind
564    entry_dict = self._dictionary_by_name(parent.entries)
565    for p in parent._leafs:
566      ins_list = p.get_inner_namespace_list()
567
568      if len(ins_list) == depth:
569        entry = entry_dict.get(p.name, p)
570        entry_dict[p.name] = entry
571
572    for name, entry in entry_dict.items():
573
574      old_parent = entry.parent
575      entry._parent = parent
576
577      if entry not in parent.entries:
578        parent._entries.append(entry)
579
580      if old_parent is not None and old_parent != parent:
581        print("ERROR: Parent changed from '%s' to '%s' for entry '%s'"
582              % (old_parent.name, parent.name, entry.name), file = sys.stderr)
583
584  def _get_children(self):
585    if self.outer_namespaces is not None:
586      for i in self.outer_namespaces:
587        yield i
588
589    if self.tags is not None:
590      for i in self.tags:
591        yield i
592
593class Tag(Node):
594  """
595  A tag Node corresponding to a top-level <tag> element.
596
597  Attributes (Read-Only):
598    name: alias for id
599    id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC'
600    description: The description of the tag, the contents of the <tag> element.
601    parent: An edge to the parent, which is always the Metadata root node.
602    entries: A sequence of edges to entries/clones that are using this Tag.
603  """
604  def __init__(self, name, parent, description=""):
605    self._name        = name  # 'id' attribute in XML
606    self._id          = name
607    self._description = description
608    self._parent      = parent
609
610    # all entries that have this tag, including clones
611    self._entries     = []  # filled in by Metadata#construct_tags
612
613  @property
614  def id(self):
615    return self._id
616
617  @property
618  def description(self):
619    return self._description
620
621  @property
622  def entries(self):
623    return (i for i in self._entries)
624
625  def _get_children(self):
626    return None
627
628class Typedef(Node):
629  """
630  A typedef Node corresponding to a <typedef> element under a top-level <types>.
631
632  Attributes (Read-Only):
633    name: The name of this typedef as a string.
634    languages: A dictionary of 'language name' -> 'fully qualified class'.
635    parent: An edge to the parent, which is always the Metadata root node.
636    entries: An iterable over all entries which reference this typedef.
637  """
638  def __init__(self, name, parent, languages=None):
639    self._name        = name
640    self._parent      = parent
641
642    # all entries that have this typedef
643    self._entries     = []  # filled in by Metadata#construct_types
644
645    self._languages   = languages or {}
646
647  @property
648  def languages(self):
649    return self._languages
650
651  @property
652  def entries(self):
653    return (i for i in self._entries)
654
655  def _get_children(self):
656    return None
657
658class OuterNamespace(Node):
659  """
660  A node corresponding to a <namespace> element under <metadata>
661
662  Attributes (Read-Only):
663    name: The name attribute of the <namespace name="foo"> element.
664    parent: An edge to the parent, which is always the Metadata root node.
665    sections: A sequence of Section children.
666  """
667  def __init__(self, name, parent, sections=[]):
668    self._name = name
669    self._parent = parent # MetadataSet
670    self._sections = sections[:]
671    self._leafs = []
672
673    self._children = self._sections
674
675  @property
676  def sections(self):
677    return (i for i in self._sections)
678
679class Section(Node):
680  """
681  A node corresponding to a <section> element under <namespace>
682
683  Attributes (Read-Only):
684    name: The name attribute of the <section name="foo"> element.
685    parent: An edge to the parent, which is always an OuterNamespace instance.
686    description: A string description of the section, or None.
687    kinds: A sequence of Kind children.
688    merged_kinds: A sequence of virtual Kind children,
689                  with each Kind's children merged by the kind.name
690    hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
691  """
692  def __init__(self, name, parent, description=None, kinds=[]):
693    self._name = name
694    self._parent = parent
695    self._description = description
696    self._kinds = kinds[:]
697
698    self._leafs = []
699
700  @property
701  def description(self):
702    return self._description
703
704  @property
705  def kinds(self):
706    return (i for i in self._kinds)
707
708  @property
709  def hal_versions(self):
710    hal_versions = set()
711    for i in self._kinds:
712      for entry in i.entries:
713        hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
714      for namespace in i.namespaces:
715        hal_versions.update(namespace.hal_versions)
716    return hal_versions
717
718  def sort_children(self):
719    self.validate_tree()
720    # order is always controls,static,dynamic
721    find_child = lambda x: [i for i in self._get_children() if i.name == x]
722    new_lst = find_child('controls') \
723            + find_child('static')   \
724            + find_child('dynamic')
725    self._kinds = new_lst
726    self.validate_tree()
727
728  def _get_children(self):
729    return (i for i in self.kinds)
730
731  @property
732  def merged_kinds(self):
733
734    def aggregate_by_name(acc, el):
735      existing = [i for i in acc if i.name == el.name]
736      if existing:
737        k = existing[0]
738      else:
739        k = Kind(el.name, el.parent)
740        acc.append(k)
741
742      k._namespaces.extend(el._namespaces)
743      k._entries.extend(el._entries)
744
745      return acc
746
747    new_kinds_lst = functools.reduce(aggregate_by_name, self.kinds, [])
748
749    for k in new_kinds_lst:
750      yield k
751
752  def combine_kinds_into_single_node(self):
753    r"""
754    Combines the section's Kinds into a single node.
755
756    Combines all the children (kinds) of this section into a single
757    virtual Kind node.
758
759    Returns:
760      A new Kind node that collapses all Kind siblings into one, combining
761      all their children together.
762
763      For example, given self.kinds == [ x, y ]
764
765        x  y               z
766      / |  | \    -->   / | | \
767      a b  c d          a b c d
768
769      a new instance z is returned in this example.
770
771    Remarks:
772      The children of the kinds are the same references as before, that is
773      their parents will point to the old parents and not to the new parent.
774    """
775    combined = Kind(name="combined", parent=self)
776
777    for k in self._get_children():
778      combined._namespaces.extend(k.namespaces)
779      combined._entries.extend(k.entries)
780
781    return combined
782
783class Kind(Node):
784  """
785  A node corresponding to one of: <static>,<dynamic>,<controls> under a
786  <section> element.
787
788  Attributes (Read-Only):
789    name: A string which is one of 'static', 'dynamic, or 'controls'.
790    parent: An edge to the parent, which is always a Section  instance.
791    namespaces: A sequence of InnerNamespace children.
792    entries: A sequence of Entry/Clone children.
793    merged_entries: A sequence of MergedEntry virtual nodes from entries
794  """
795  def __init__(self, name, parent):
796    self._name = name
797    self._parent = parent
798    self._namespaces = []
799    self._entries = []
800
801    self._leafs = []
802
803  @property
804  def namespaces(self):
805    return self._namespaces
806
807  @property
808  def entries(self):
809    return self._entries
810
811  @property
812  def merged_entries(self):
813    for i in self.entries:
814      yield i.merge()
815
816  def sort_children(self):
817    self._namespaces.sort(key=self._get_name())
818    self._entries.sort(key=self._get_name())
819
820  def _get_children(self):
821    for i in self.namespaces:
822      yield i
823    for i in self.entries:
824      yield i
825
826  def combine_children_by_name(self):
827    r"""
828    Combine multiple children with the same name into a single node.
829
830    Returns:
831      A new Kind where all of the children with the same name were combined.
832
833      For example:
834
835      Given a Kind k:
836
837              k
838            / | \
839            a b c
840            | | |
841            d e f
842
843      a.name == "foo"
844      b.name == "foo"
845      c.name == "bar"
846
847      The returned Kind will look like this:
848
849             k'
850            /  \
851            a' c'
852          / |  |
853          d e  f
854
855    Remarks:
856      This operation is not recursive. To combine the grandchildren and other
857      ancestors, call this method on the ancestor nodes.
858    """
859    return Kind._combine_children_by_name(self, new_type=type(self))
860
861  # new_type is either Kind or InnerNamespace
862  @staticmethod
863  def _combine_children_by_name(self, new_type):
864    new_ins_dict = OrderedDict()
865    new_ent_dict = OrderedDict()
866
867    for ins in self.namespaces:
868      new_ins = new_ins_dict.setdefault(ins.name,
869                                        InnerNamespace(ins.name, parent=self))
870      new_ins._namespaces.extend(ins.namespaces)
871      new_ins._entries.extend(ins.entries)
872
873    for ent in self.entries:
874      new_ent = new_ent_dict.setdefault(ent.name,
875                                        ent.merge())
876
877    kind = new_type(self.name, self.parent)
878    kind._namespaces = new_ins_dict.values()
879    kind._entries = new_ent_dict.values()
880
881    return kind
882
883class InnerNamespace(Node):
884  """
885  A node corresponding to a <namespace> which is an ancestor of a Kind.
886  These namespaces may have other namespaces recursively, or entries as leafs.
887
888  Attributes (Read-Only):
889    name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
890    parent: An edge to the parent, which is an InnerNamespace or a Kind.
891    namespaces: A sequence of InnerNamespace children.
892    entries: A sequence of Entry/Clone children.
893    merged_entries: A sequence of MergedEntry virtual nodes from entries
894    hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
895  """
896  def __init__(self, name, parent):
897    self._name        = name
898    self._parent      = parent
899    self._namespaces  = []
900    self._entries     = []
901    self._leafs       = []
902
903  @property
904  def namespaces(self):
905    return self._namespaces
906
907  @property
908  def entries(self):
909    return self._entries
910
911  @property
912  def hal_versions(self):
913    hal_versions = set()
914    for entry in self.entries:
915      hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
916    for namespace in self.namespaces:
917      hal_versions.update(namespace.hal_versions)
918    return hal_versions
919
920  @property
921  def merged_entries(self):
922    for i in self.entries:
923      yield i.merge()
924
925  def sort_children(self):
926    self._namespaces.sort(key=self._get_name())
927    self._entries.sort(key=self._get_name())
928
929  def _get_children(self):
930    for i in self.namespaces:
931      yield i
932    for i in self.entries:
933      yield i
934
935  def combine_children_by_name(self):
936    r"""
937    Combine multiple children with the same name into a single node.
938
939    Returns:
940      A new InnerNamespace where all of the children with the same name were
941      combined.
942
943      For example:
944
945      Given an InnerNamespace i:
946
947              i
948            / | \
949            a b c
950            | | |
951            d e f
952
953      a.name == "foo"
954      b.name == "foo"
955      c.name == "bar"
956
957      The returned InnerNamespace will look like this:
958
959             i'
960            /  \
961            a' c'
962          / |  |
963          d e  f
964
965    Remarks:
966      This operation is not recursive. To combine the grandchildren and other
967      ancestors, call this method on the ancestor nodes.
968    """
969    return Kind._combine_children_by_name(self, new_type=type(self))
970
971class EnumValue(Node):
972  """
973  A class corresponding to a <value> element within an <enum> within an <entry>.
974
975  Attributes (Read-Only):
976    name: A string,                 e.g. 'ON' or 'OFF'
977    id: An optional numeric string, e.g. '0' or '0xFF'
978    deprecated: A boolean, True if the enum should be deprecated.
979    optional: A boolean
980    visibility: A string, one of "system", "java_public", "ndk_public", "hidden", "public",
981                "fwk_java_public", "fwk_public", "fwk_ndk_public", "extension", "fwk_system_public"
982    notes: A string describing the notes, or None.
983    sdk_notes: A string describing extra notes for public SDK only
984    ndk_notes: A string describing extra notes for public NDK only
985    parent: An edge to the parent, always an Enum instance.
986    hal_major_version: The major HIDL HAL version this value was first added in
987    hal_minor_version: The minor HIDL HAL version this value was first added in
988    aconfig_flag: The aconfig flag name that determines if this enum value is actually enabled
989  """
990  def __init__(self, name, parent,
991               id=None, deprecated=False, optional=False, visibility=None, notes=None,
992               sdk_notes=None, ndk_notes=None, hal_version='3.2', aconfig_flag=None):
993    self._name = name                    # str, e.g. 'ON' or 'OFF'
994    self._id = id                        # int, e.g. '0'
995    self._deprecated = deprecated        # bool
996    self._optional = optional            # bool
997    self._visibility = visibility        # None or str; None is same as public
998    self._notes = notes                  # None or str
999    self._sdk_notes = sdk_notes          # None or str
1000    self._ndk_notes = ndk_notes          # None or str
1001    self._parent = parent
1002    if hal_version is None:
1003      if parent is not None and parent.parent is not None:
1004        self._hal_major_version = parent.parent.hal_major_version
1005        self._hal_minor_version = parent.parent.hal_minor_version
1006      else:
1007        self._hal_major_version = 3
1008        self._hal_minor_version = 2
1009    else:
1010      self._hal_major_version = int(hal_version.partition('.')[0])
1011      self._hal_minor_version = int(hal_version.partition('.')[2])
1012
1013    self._aconfig_flag = aconfig_flag
1014    if self._aconfig_flag is None:
1015      if parent is not None and parent.parent is not None:
1016        self._aconfig_flag = parent.parent.aconfig_flag
1017
1018  @property
1019  def id(self):
1020    return self._id
1021
1022  @property
1023  def deprecated(self):
1024    return self._deprecated
1025
1026  @property
1027  def optional(self):
1028    return self._optional
1029
1030  @property
1031  def visibility(self):
1032    return self._visibility
1033
1034  @property
1035  def applied_visibility(self):
1036    return self._visibility or 'public'
1037
1038  @property
1039  def hidl_comment_string(self):
1040    parent_enum = None
1041    if (self.parent is not None and self.parent.parent is not None):
1042      parent_enum = self.parent.parent
1043    if parent_enum is not None and parent_enum.visibility in ('fwk_only', 'fwk_java_public',\
1044        'fwk_public', 'fwk_ndk_public') or self._visibility in ('fwk_only', 'fwk_java_public',\
1045        'fwk_public', 'fwk_ndk_public'):
1046      return ','
1047    return ', // HIDL v' + str(self._hal_major_version) + '.' + str(self.hal_minor_version)
1048
1049  @property
1050  def hidden(self):
1051    return self.visibility in {'hidden', 'ndk_public', 'test', 'extension', 'fwk_system_public'}
1052
1053  @property
1054  def ndk_hidden(self):
1055    return self._visibility in {'hidden', 'java_public', 'test'}
1056
1057  @property
1058  def notes(self):
1059    return self._notes
1060
1061  @property
1062  def sdk_notes(self):
1063    return self._sdk_notes
1064
1065  @property
1066  def ndk_notes(self):
1067    return self._ndk_notes
1068
1069  @property
1070  def hal_major_version(self):
1071    return self._hal_major_version
1072
1073  @property
1074  def hal_minor_version(self):
1075    return self._hal_minor_version
1076
1077  @property
1078  def aconfig_flag(self):
1079    return self._aconfig_flag
1080
1081  def _get_children(self):
1082    return None
1083
1084class Enum(Node):
1085  """
1086  A class corresponding to an <enum> element within an <entry>.
1087
1088  Attributes (Read-Only):
1089    parent: An edge to the parent, always an Entry instance.
1090    values: A sequence of EnumValue children.
1091    has_values_with_id: A boolean representing if any of the children have a
1092        non-empty id property.
1093  """
1094  def __init__(self, parent, values, ids={}, deprecateds=[],
1095               optionals=[], visibilities={}, notes={}, sdk_notes={}, ndk_notes={},
1096               hal_versions={}, aconfig_flags={}):
1097    self._parent = parent
1098    self._name = None
1099    self._values =                                                             \
1100      [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, visibilities.get(val), \
1101                  notes.get(val), sdk_notes.get(val), ndk_notes.get(val), hal_versions.get(val),        \
1102                  aconfig_flags.get(val)) \
1103        for val in values ]
1104
1105  @property
1106  def values(self):
1107    return (i for i in self._values)
1108
1109  @property
1110  def has_values_with_id(self):
1111    return bool(any(i for i in self.values if i.id))
1112
1113  def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
1114    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))
1115
1116  def _get_children(self):
1117    return (i for i in self._values)
1118
1119class Entry(Node):
1120  """
1121  A node corresponding to an <entry> element.
1122
1123  Attributes (Read-Only):
1124    parent: An edge to the parent node, which is an InnerNamespace or Kind.
1125    name: The fully qualified name string, e.g. 'android.shading.mode'
1126    name_short: The name attribute from <entry name="mode">, e.g. mode
1127    type: The type attribute from <entry type="bar">
1128    kind: A string ('static', 'dynamic', 'controls') corresponding to the
1129          ancestor Kind#name
1130    container: The container attribute from <entry container="array">, or None.
1131    container_sizes: A sequence of size strings or None if container is None.
1132    enum: An Enum instance if the enum attribute is true, None otherwise.
1133    visibility: The visibility of this entry ('system', 'hidden', 'public' etc)
1134                across the system. System entries are only visible in native code
1135                headers. Hidden entries are marked @hide in managed code, while
1136                public entries are visible in the Android SDK.
1137    applied_visibility: As visibility, but always valid, defaulting to 'system'
1138                        if no visibility is given for an entry.
1139    applied_ndk_visible: Always valid. Default is 'false'.
1140                         Set to 'true' when the visibility implied entry is visible
1141                         in NDK.
1142    synthetic: The C-level visibility of this entry ('false', 'true').
1143               Synthetic entries will not be generated into the native metadata
1144               list of entries (in C code). In general a synthetic entry is
1145               glued together at the Java layer from multiple visibiltity=hidden
1146               entries.
1147    hwlevel: The lowest hardware level at which the entry is guaranteed
1148             to be supported by the camera device. All devices with higher
1149             hwlevels will also include this entry. None means that the
1150             entry is optional on any hardware level.
1151    permission_needed: Flags whether the tag needs extra camera permission.
1152    aconfig_flag: The aconfig flag name that determines if the entry is actually enabled
1153    deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1154               unreleased version this needs to be removed altogether. If applied
1155               to an entry from an older release, then this means the entry
1156               should be ignored by newer code.
1157    optional: a bool representing the optional attribute, which denotes the entry
1158              is required for hardware level full devices, but optional for other
1159              hardware levels.  None if not present.
1160    applied_optional: As optional but always valid, defaulting to False if no
1161                      optional attribute is present.
1162    tuple_values: A sequence of strings describing the tuple values,
1163                  None if container is not 'tuple'.
1164    description: A string description, or None.
1165    deprecation_description: A string describing the reason for deprecation. Must be present
1166                 if deprecated is true, otherwise may be None.
1167    range: A string range, or None.
1168    units: A string units, or None.
1169    tags: A sequence of Tag nodes associated with this Entry.
1170    type_notes: A string describing notes for the type, or None.
1171    typedef: A Typedef associated with this Entry, or None.
1172
1173  Remarks:
1174    Subclass Clone can be used interchangeable with an Entry,
1175    for when we don't care about the underlying type.
1176
1177    parent and tags edges are invalid until after Metadata#construct_graph
1178    has been invoked.
1179  """
1180  def __init__(self, **kwargs):
1181    """
1182    Instantiate a new Entry node.
1183
1184    Args:
1185      name: A string with the fully qualified name, e.g. 'android.shading.mode'
1186      type: A string describing the type, e.g. 'int32'
1187      kind: A string describing the kind, e.g. 'static'
1188      hal_version: A string for the initial HIDL HAL metadata version this entry
1189                   was added in
1190
1191    Args (if container):
1192      container: A string describing the container, e.g. 'array' or 'tuple'
1193      container_sizes: A list of string sizes if a container, or None otherwise
1194
1195    Args (if container is 'tuple'):
1196      tuple_values: A list of tuple values, e.g. ['width', 'height']
1197
1198    Args (if the 'enum' attribute is true):
1199      enum: A boolean, True if this is an enum, False otherwise
1200      enum_values: A list of value strings, e.g. ['ON', 'OFF']
1201      enum_optionals: A list of optional enum values, e.g. ['OFF']
1202      enum_notes: A dictionary of value->notes strings.
1203      enum_ids: A dictionary of value->id strings.
1204      enum_hal_versions: A dictionary of value->hal version strings
1205      enum_aconfig_flags: A dictionary of value->aconfig flag name strings
1206
1207    Args (if the 'deprecated' attribute is true):
1208      deprecation_description: A string explaining the deprecation, to be added
1209                               to the Java-layer @deprecated tag
1210
1211    Args (optional):
1212      description: A string with a description of the entry.
1213      range: A string with the range of the values of the entry, e.g. '>= 0'
1214      units: A string with the units of the values, e.g. 'inches'
1215      details: A string with the detailed documentation for the entry
1216      hal_details: A string with the HAL implementation details for the entry
1217      ndk_details: A string with the extra NDK API documentation for the entry=
1218      tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1219      type_notes: A string with the notes for the type
1220      visibility: A string describing the visibility, eg 'system', 'hidden',
1221                  'public'
1222      synthetic: A bool to mark whether this entry is visible only at the Java
1223                 layer (True), or at both layers (False = default).
1224      hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
1225      deprecated: A bool to mark whether this is @Deprecated at the Java layer
1226                 (default = False).
1227      optional: A bool to mark whether optional for non-full hardware devices
1228      typedef: A string corresponding to a typedef's name attribute.
1229    """
1230
1231    if kwargs.get('type') is None:
1232      print("ERROR: Missing type for entry '%s' kind '%s'"
1233            % (kwargs.get('name'), kwargs.get('kind')), file=sys.stderr)
1234
1235    # Attributes are Read-Only, but edges may be mutated by
1236    # Metadata, particularly during construct_graph
1237
1238    self._name = kwargs['name']
1239    self._type = kwargs['type']
1240    self._kind = kwargs['kind'] # static, dynamic, or controls
1241
1242    self._init_common(**kwargs)
1243
1244  @property
1245  def type(self):
1246    return self._type
1247
1248  @property
1249  def kind(self):
1250    return self._kind
1251
1252  @property
1253  def hal_major_version(self):
1254    return self._hal_major_version
1255
1256  @property
1257  def hal_minor_version(self):
1258    return self._hal_minor_version
1259
1260  @property
1261  def visibility(self):
1262    return self._visibility
1263
1264  @property
1265  def applied_visibility(self):
1266    return self._visibility or 'system'
1267
1268  @property
1269  def hidl_comment_string(self):
1270    if self._visibility in ('fwk_only', 'fwk_java_public', 'fwk_public', 'fwk_ndk_public'):
1271      return self._visibility
1272    visibility_lj = str(self.applied_visibility).ljust(12)
1273    return visibility_lj + ' | HIDL v' + str(self._hal_major_version) + '.' + str(self._hal_minor_version)
1274
1275  @property
1276  def applied_ndk_visible(self):
1277    if self._visibility in ("public", "ndk_public", "fwk_public", "fwk_ndk_public"):
1278      return "true"
1279    return "false"
1280
1281  @property
1282  def synthetic(self):
1283    return self._synthetic
1284
1285  @property
1286  def hwlevel(self):
1287    return self._hwlevel
1288
1289  @property
1290  def deprecated(self):
1291    return self._deprecated
1292
1293  @property
1294  def deprecation_description(self):
1295    return self._deprecation_description
1296
1297  @property
1298  def permission_needed(self):
1299    return self._permission_needed or "false"
1300
1301  @property
1302  def aconfig_flag(self):
1303    return self._aconfig_flag
1304
1305  # TODO: optional should just return hwlevel is None
1306  @property
1307  def optional(self):
1308    return self._optional
1309
1310  @property
1311  def applied_optional(self):
1312    return self._optional or False
1313
1314  @property
1315  def name_short(self):
1316    return self.get_name_minimal()
1317
1318  @property
1319  def container(self):
1320    return self._container
1321
1322  @property
1323  def container_sizes(self):
1324    if self._container_sizes is None:
1325      return None
1326    else:
1327      return (i for i in self._container_sizes)
1328
1329  @property
1330  def tuple_values(self):
1331    if self._tuple_values is None:
1332      return None
1333    else:
1334      return (i for i in self._tuple_values)
1335
1336  @property
1337  def description(self):
1338    return self._description
1339
1340  @property
1341  def range(self):
1342    return self._range
1343
1344  @property
1345  def units(self):
1346    return self._units
1347
1348  @property
1349  def details(self):
1350    return self._details
1351
1352  @property
1353  def hal_details(self):
1354    return self._hal_details
1355
1356  @property
1357  def ndk_details(self):
1358    return self._ndk_details
1359
1360  @property
1361  def applied_ndk_details(self):
1362    return (self._details or "") + (self._ndk_details or "")
1363
1364  @property
1365  def tags(self):
1366    if self._tags is None:
1367      return None
1368    else:
1369      return (i for i in self._tags)
1370
1371  @property
1372  def type_notes(self):
1373    return self._type_notes
1374
1375  @property
1376  def typedef(self):
1377    return self._typedef
1378
1379  @property
1380  def enum(self):
1381    return self._enum
1382
1383  @property
1384  def session_characteristics_key_since(self):
1385    return self._session_characteristics_key_since
1386
1387  def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
1388    if self._enum is not None:
1389      return self._enum.has_new_values_added_in_hal_version(hal_major_version,hal_minor_version)
1390    else:
1391      return False
1392
1393  def _get_children(self):
1394    if self.enum:
1395      yield self.enum
1396
1397  def sort_children(self):
1398    return None
1399
1400  def is_clone(self):
1401    """
1402    Whether or not this is a Clone instance.
1403
1404    Returns:
1405      False
1406    """
1407    return False
1408
1409  def _init_common(self, **kwargs):
1410
1411    self._parent = None # filled in by Metadata::_construct_entries
1412
1413    self._container = kwargs.get('container')
1414    self._container_sizes = kwargs.get('container_sizes')
1415
1416    hal_version = kwargs.get('hal_version')
1417    if hal_version is None:
1418      if self.is_clone():
1419        self._hal_major_version = 0
1420        self._hal_minor_version = 0
1421      else:
1422        self._hal_major_version = 3
1423        self._hal_minor_version = 2
1424    else:
1425      self._hal_major_version = int(hal_version.partition('.')[0])
1426      self._hal_minor_version = int(hal_version.partition('.')[2])
1427
1428    self._aconfig_flag = kwargs.get('aconfig_flag')
1429
1430    # access these via the 'enum' prop
1431    enum_values = kwargs.get('enum_values')
1432    enum_deprecateds = kwargs.get('enum_deprecateds')
1433    enum_optionals = kwargs.get('enum_optionals')
1434    enum_visibilities = kwargs.get('enum_visibilities')
1435    enum_notes = kwargs.get('enum_notes')  # { value => notes }
1436    enum_sdk_notes = kwargs.get('enum_sdk_notes')  # { value => sdk_notes }
1437    enum_ndk_notes = kwargs.get('enum_ndk_notes')  # { value => ndk_notes }
1438    enum_ids = kwargs.get('enum_ids')  # { value => notes }
1439    enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions }
1440    enum_aconfig_flags = kwargs.get('enum_aconfig_flags') # { value => aconfig flags }
1441
1442    self._tuple_values = kwargs.get('tuple_values')
1443
1444    self._description = kwargs.get('description')
1445    self._range = kwargs.get('range')
1446    self._units = kwargs.get('units')
1447    self._details = kwargs.get('details')
1448    self._hal_details = kwargs.get('hal_details')
1449    self._ndk_details = kwargs.get('ndk_details')
1450
1451    self._tag_ids = kwargs.get('tag_ids', [])
1452    self._tags = None  # Filled in by Metadata::_construct_tags
1453
1454    self._type_notes = kwargs.get('type_notes')
1455    self._type_name = kwargs.get('type_name')
1456    self._typedef = None # Filled in by Metadata::_construct_types
1457
1458    if kwargs.get('enum', False):
1459      self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
1460                        enum_visibilities, enum_notes, enum_sdk_notes, enum_ndk_notes,
1461                        enum_hal_versions, enum_aconfig_flags)
1462    else:
1463      self._enum = None
1464
1465    self._visibility = kwargs.get('visibility')
1466    self._synthetic = kwargs.get('synthetic', False)
1467    self._hwlevel = kwargs.get('hwlevel')
1468    self._deprecated = kwargs.get('deprecated', False)
1469    self._deprecation_description = kwargs.get('deprecation_description')
1470
1471    self._permission_needed = kwargs.get('permission_needed')
1472    self._optional = kwargs.get('optional')
1473    self._ndk_visible = kwargs.get('ndk_visible')
1474
1475    self._session_characteristics_key_since = None \
1476                  if not kwargs.get('session_characteristics_key_since') \
1477                    else int(kwargs.get('session_characteristics_key_since'))
1478
1479    self._property_keys = kwargs
1480
1481  def merge(self):
1482    """
1483    Copy the attributes into a new entry, merging it with the target entry
1484    if it's a clone.
1485    """
1486    return MergedEntry(self)
1487
1488  # Helpers for accessing less than the fully qualified name
1489
1490  def get_name_as_list(self):
1491    """
1492    Returns the name as a list split by a period.
1493
1494    For example:
1495      entry.name is 'android.lens.info.shading'
1496      entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1497    """
1498    return self.name.split(".")
1499
1500  def get_inner_namespace_list(self):
1501    """
1502    Returns the inner namespace part of the name as a list
1503
1504    For example:
1505      entry.name is 'android.lens.info.shading'
1506      entry.get_inner_namespace_list() == ['info']
1507    """
1508    return self.get_name_as_list()[2:-1]
1509
1510  def get_outer_namespace(self):
1511    """
1512    Returns the outer namespace as a string.
1513
1514    For example:
1515      entry.name is 'android.lens.info.shading'
1516      entry.get_outer_namespace() == 'android'
1517
1518    Remarks:
1519      Since outer namespaces are non-recursive,
1520      and each entry has one, this does not need to be a list.
1521    """
1522    return self.get_name_as_list()[0]
1523
1524  def get_section(self):
1525    """
1526    Returns the section as a string.
1527
1528    For example:
1529      entry.name is 'android.lens.info.shading'
1530      entry.get_section() == ''
1531
1532    Remarks:
1533      Since outer namespaces are non-recursive,
1534      and each entry has one, this does not need to be a list.
1535    """
1536    return self.get_name_as_list()[1]
1537
1538  def get_name_minimal(self):
1539    """
1540    Returns only the last component of the fully qualified name as a string.
1541
1542    For example:
1543      entry.name is 'android.lens.info.shading'
1544      entry.get_name_minimal() == 'shading'
1545
1546    Remarks:
1547      entry.name_short it an alias for this
1548    """
1549    return self.get_name_as_list()[-1]
1550
1551  def get_path_without_name(self):
1552    """
1553    Returns a string path to the entry, with the name component excluded.
1554
1555    For example:
1556      entry.name is 'android.lens.info.shading'
1557      entry.get_path_without_name() == 'android.lens.info'
1558    """
1559    return ".".join(self.get_name_as_list()[0:-1])
1560
1561
1562class Clone(Entry):
1563  """
1564  A Node corresponding to a <clone> element. It has all the attributes of an
1565  <entry> element (Entry) plus the additions specified below.
1566
1567  Attributes (Read-Only):
1568    entry: an edge to an Entry object that this targets
1569    target_kind: A string describing the kind of the target entry.
1570    name: a string of the name, same as entry.name
1571    kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1572          for the <clone> element.
1573    type: always None, since a clone cannot override the type.
1574  """
1575  def __init__(self, entry=None, **kwargs):
1576    """
1577    Instantiate a new Clone node.
1578
1579    Args:
1580      name: A string with the fully qualified name, e.g. 'android.shading.mode'
1581      type: A string describing the type, e.g. 'int32'
1582      kind: A string describing the kind, e.g. 'static'
1583      target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1584      hal_version: A string for the initial HIDL HAL metadata version this entry
1585                   was added in
1586
1587    Args (if container):
1588      container: A string describing the container, e.g. 'array' or 'tuple'
1589      container_sizes: A list of string sizes if a container, or None otherwise
1590
1591    Args (if container is 'tuple'):
1592      tuple_values: A list of tuple values, e.g. ['width', 'height']
1593
1594    Args (if the 'enum' attribute is true):
1595      enum: A boolean, True if this is an enum, False otherwise
1596      enum_values: A list of value strings, e.g. ['ON', 'OFF']
1597      enum_optionals: A list of optional enum values, e.g. ['OFF']
1598      enum_notes: A dictionary of value->notes strings.
1599      enum_ids: A dictionary of value->id strings.
1600
1601    Args (optional):
1602      entry: An edge to the corresponding target Entry.
1603      description: A string with a description of the entry.
1604      range: A string with the range of the values of the entry, e.g. '>= 0'
1605      units: A string with the units of the values, e.g. 'inches'
1606      details: A string with the detailed documentation for the entry
1607      hal_details: A string with the HAL implementation details for the entry
1608      ndk_details: A string with the extra NDK documentation for the entry
1609      tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1610      type_notes: A string with the notes for the type
1611
1612    Remarks:
1613      Note that type is not specified since it has to be the same as the
1614      entry.type.
1615    """
1616    self._entry = entry  # Entry object
1617    self._target_kind = kwargs['target_kind']
1618    self._name = kwargs['name']  # same as entry.name
1619    self._kind = kwargs['kind']
1620
1621    # illegal to override the type, it should be the same as the entry
1622    self._type = None
1623    # the rest of the kwargs are optional
1624    # can be used to override the regular entry data
1625    self._init_common(**kwargs)
1626
1627  @property
1628  def entry(self):
1629    return self._entry
1630
1631  @property
1632  def target_kind(self):
1633    return self._target_kind
1634
1635  def is_clone(self):
1636    """
1637    Whether or not this is a Clone instance.
1638
1639    Returns:
1640      True
1641    """
1642    return True
1643
1644class MergedEntry(Entry):
1645  """
1646  A MergedEntry has all the attributes of a Clone and its target Entry merged
1647  together.
1648
1649  Remarks:
1650    Useful when we want to 'unfold' a clone into a real entry by copying out
1651    the target entry data. In this case we don't care about distinguishing
1652    a clone vs an entry.
1653  """
1654  def __init__(self, entry):
1655    """
1656    Create a new instance of MergedEntry.
1657
1658    Args:
1659      entry: An Entry or Clone instance
1660    """
1661    props_distinct = ['description', 'units', 'range', 'details',
1662                      'hal_details', 'ndk_details', 'tags', 'kind',
1663                      'deprecation_description']
1664
1665    for p in props_distinct:
1666      p = '_' + p
1667      if entry.is_clone():
1668        setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1669      else:
1670        setattr(self, p, getattr(entry, p))
1671
1672    props_common = ['parent', 'name', 'container',
1673                    'container_sizes', 'enum',
1674                    'tuple_values',
1675                    'type',
1676                    'type_notes',
1677                    'visibility',
1678                    'ndk_visible',
1679                    'synthetic',
1680                    'hwlevel',
1681                    'deprecated',
1682                    'optional',
1683                    'typedef',
1684                    'hal_major_version',
1685                    'hal_minor_version',
1686                    'permission_needed',
1687                    'aconfig_flag',
1688                    'session_characteristics_key_since'
1689                   ]
1690
1691    for p in props_common:
1692      p = '_' + p
1693      if entry.is_clone():
1694        setattr(self, p, getattr(entry.entry, p))
1695      else:
1696        setattr(self, p, getattr(entry, p))
1697