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", "extension"
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        or self._visibility in ('fwk_only', 'fwk_java_public'):
1045      return ','
1046    return ', // HIDL v' + str(self._hal_major_version) + '.' + str(self.hal_minor_version)
1047
1048  @property
1049  def hidden(self):
1050    return self.visibility in {'hidden', 'ndk_public', 'test', 'extension'}
1051
1052  @property
1053  def ndk_hidden(self):
1054    return self._visibility in {'hidden', 'java_public', 'test'}
1055
1056  @property
1057  def notes(self):
1058    return self._notes
1059
1060  @property
1061  def sdk_notes(self):
1062    return self._sdk_notes
1063
1064  @property
1065  def ndk_notes(self):
1066    return self._ndk_notes
1067
1068  @property
1069  def hal_major_version(self):
1070    return self._hal_major_version
1071
1072  @property
1073  def hal_minor_version(self):
1074    return self._hal_minor_version
1075
1076  @property
1077  def aconfig_flag(self):
1078    return self._aconfig_flag
1079
1080  def _get_children(self):
1081    return None
1082
1083class Enum(Node):
1084  """
1085  A class corresponding to an <enum> element within an <entry>.
1086
1087  Attributes (Read-Only):
1088    parent: An edge to the parent, always an Entry instance.
1089    values: A sequence of EnumValue children.
1090    has_values_with_id: A boolean representing if any of the children have a
1091        non-empty id property.
1092  """
1093  def __init__(self, parent, values, ids={}, deprecateds=[],
1094               optionals=[], visibilities={}, notes={}, sdk_notes={}, ndk_notes={},
1095               hal_versions={}, aconfig_flags={}):
1096    self._parent = parent
1097    self._name = None
1098    self._values =                                                             \
1099      [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, visibilities.get(val), \
1100                  notes.get(val), sdk_notes.get(val), ndk_notes.get(val), hal_versions.get(val),        \
1101                  aconfig_flags.get(val)) \
1102        for val in values ]
1103
1104  @property
1105  def values(self):
1106    return (i for i in self._values)
1107
1108  @property
1109  def has_values_with_id(self):
1110    return bool(any(i for i in self.values if i.id))
1111
1112  def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
1113    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))
1114
1115  def _get_children(self):
1116    return (i for i in self._values)
1117
1118class Entry(Node):
1119  """
1120  A node corresponding to an <entry> element.
1121
1122  Attributes (Read-Only):
1123    parent: An edge to the parent node, which is an InnerNamespace or Kind.
1124    name: The fully qualified name string, e.g. 'android.shading.mode'
1125    name_short: The name attribute from <entry name="mode">, e.g. mode
1126    type: The type attribute from <entry type="bar">
1127    kind: A string ('static', 'dynamic', 'controls') corresponding to the
1128          ancestor Kind#name
1129    container: The container attribute from <entry container="array">, or None.
1130    container_sizes: A sequence of size strings or None if container is None.
1131    enum: An Enum instance if the enum attribute is true, None otherwise.
1132    visibility: The visibility of this entry ('system', 'hidden', 'public')
1133                across the system. System entries are only visible in native code
1134                headers. Hidden entries are marked @hide in managed code, while
1135                public entries are visible in the Android SDK.
1136    applied_visibility: As visibility, but always valid, defaulting to 'system'
1137                        if no visibility is given for an entry.
1138    applied_ndk_visible: Always valid. Default is 'false'.
1139                         Set to 'true' when the visibility implied entry is visible
1140                         in NDK.
1141    synthetic: The C-level visibility of this entry ('false', 'true').
1142               Synthetic entries will not be generated into the native metadata
1143               list of entries (in C code). In general a synthetic entry is
1144               glued together at the Java layer from multiple visibiltity=hidden
1145               entries.
1146    hwlevel: The lowest hardware level at which the entry is guaranteed
1147             to be supported by the camera device. All devices with higher
1148             hwlevels will also include this entry. None means that the
1149             entry is optional on any hardware level.
1150    permission_needed: Flags whether the tag needs extra camera permission.
1151    aconfig_flag: The aconfig flag name that determines if the entry is actually enabled
1152    deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1153               unreleased version this needs to be removed altogether. If applied
1154               to an entry from an older release, then this means the entry
1155               should be ignored by newer code.
1156    optional: a bool representing the optional attribute, which denotes the entry
1157              is required for hardware level full devices, but optional for other
1158              hardware levels.  None if not present.
1159    applied_optional: As optional but always valid, defaulting to False if no
1160                      optional attribute is present.
1161    tuple_values: A sequence of strings describing the tuple values,
1162                  None if container is not 'tuple'.
1163    description: A string description, or None.
1164    deprecation_description: A string describing the reason for deprecation. Must be present
1165                 if deprecated is true, otherwise may be None.
1166    range: A string range, or None.
1167    units: A string units, or None.
1168    tags: A sequence of Tag nodes associated with this Entry.
1169    type_notes: A string describing notes for the type, or None.
1170    typedef: A Typedef associated with this Entry, or None.
1171
1172  Remarks:
1173    Subclass Clone can be used interchangeable with an Entry,
1174    for when we don't care about the underlying type.
1175
1176    parent and tags edges are invalid until after Metadata#construct_graph
1177    has been invoked.
1178  """
1179  def __init__(self, **kwargs):
1180    """
1181    Instantiate a new Entry node.
1182
1183    Args:
1184      name: A string with the fully qualified name, e.g. 'android.shading.mode'
1185      type: A string describing the type, e.g. 'int32'
1186      kind: A string describing the kind, e.g. 'static'
1187      hal_version: A string for the initial HIDL HAL metadata version this entry
1188                   was added in
1189
1190    Args (if container):
1191      container: A string describing the container, e.g. 'array' or 'tuple'
1192      container_sizes: A list of string sizes if a container, or None otherwise
1193
1194    Args (if container is 'tuple'):
1195      tuple_values: A list of tuple values, e.g. ['width', 'height']
1196
1197    Args (if the 'enum' attribute is true):
1198      enum: A boolean, True if this is an enum, False otherwise
1199      enum_values: A list of value strings, e.g. ['ON', 'OFF']
1200      enum_optionals: A list of optional enum values, e.g. ['OFF']
1201      enum_notes: A dictionary of value->notes strings.
1202      enum_ids: A dictionary of value->id strings.
1203      enum_hal_versions: A dictionary of value->hal version strings
1204      enum_aconfig_flags: A dictionary of value->aconfig flag name strings
1205
1206    Args (if the 'deprecated' attribute is true):
1207      deprecation_description: A string explaining the deprecation, to be added
1208                               to the Java-layer @deprecated tag
1209
1210    Args (optional):
1211      description: A string with a description of the entry.
1212      range: A string with the range of the values of the entry, e.g. '>= 0'
1213      units: A string with the units of the values, e.g. 'inches'
1214      details: A string with the detailed documentation for the entry
1215      hal_details: A string with the HAL implementation details for the entry
1216      ndk_details: A string with the extra NDK API documentation for the entry=
1217      tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1218      type_notes: A string with the notes for the type
1219      visibility: A string describing the visibility, eg 'system', 'hidden',
1220                  'public'
1221      synthetic: A bool to mark whether this entry is visible only at the Java
1222                 layer (True), or at both layers (False = default).
1223      hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
1224      deprecated: A bool to mark whether this is @Deprecated at the Java layer
1225                 (default = False).
1226      optional: A bool to mark whether optional for non-full hardware devices
1227      typedef: A string corresponding to a typedef's name attribute.
1228    """
1229
1230    if kwargs.get('type') is None:
1231      print("ERROR: Missing type for entry '%s' kind '%s'"
1232            % (kwargs.get('name'), kwargs.get('kind')), file=sys.stderr)
1233
1234    # Attributes are Read-Only, but edges may be mutated by
1235    # Metadata, particularly during construct_graph
1236
1237    self._name = kwargs['name']
1238    self._type = kwargs['type']
1239    self._kind = kwargs['kind'] # static, dynamic, or controls
1240
1241    self._init_common(**kwargs)
1242
1243  @property
1244  def type(self):
1245    return self._type
1246
1247  @property
1248  def kind(self):
1249    return self._kind
1250
1251  @property
1252  def hal_major_version(self):
1253    return self._hal_major_version
1254
1255  @property
1256  def hal_minor_version(self):
1257    return self._hal_minor_version
1258
1259  @property
1260  def visibility(self):
1261    return self._visibility
1262
1263  @property
1264  def applied_visibility(self):
1265    return self._visibility or 'system'
1266
1267  @property
1268  def hidl_comment_string(self):
1269    if self._visibility in ('fwk_only', 'fwk_java_public'):
1270      return self._visibility
1271    visibility_lj = str(self.applied_visibility).ljust(12)
1272    return visibility_lj + ' | HIDL v' + str(self._hal_major_version) + '.' + str(self._hal_minor_version)
1273
1274  @property
1275  def applied_ndk_visible(self):
1276    if self._visibility in ("public", "ndk_public"):
1277      return "true"
1278    return "false"
1279
1280  @property
1281  def synthetic(self):
1282    return self._synthetic
1283
1284  @property
1285  def hwlevel(self):
1286    return self._hwlevel
1287
1288  @property
1289  def deprecated(self):
1290    return self._deprecated
1291
1292  @property
1293  def deprecation_description(self):
1294    return self._deprecation_description
1295
1296  @property
1297  def permission_needed(self):
1298    return self._permission_needed or "false"
1299
1300  @property
1301  def aconfig_flag(self):
1302    return self._aconfig_flag
1303
1304  # TODO: optional should just return hwlevel is None
1305  @property
1306  def optional(self):
1307    return self._optional
1308
1309  @property
1310  def applied_optional(self):
1311    return self._optional or False
1312
1313  @property
1314  def name_short(self):
1315    return self.get_name_minimal()
1316
1317  @property
1318  def container(self):
1319    return self._container
1320
1321  @property
1322  def container_sizes(self):
1323    if self._container_sizes is None:
1324      return None
1325    else:
1326      return (i for i in self._container_sizes)
1327
1328  @property
1329  def tuple_values(self):
1330    if self._tuple_values is None:
1331      return None
1332    else:
1333      return (i for i in self._tuple_values)
1334
1335  @property
1336  def description(self):
1337    return self._description
1338
1339  @property
1340  def range(self):
1341    return self._range
1342
1343  @property
1344  def units(self):
1345    return self._units
1346
1347  @property
1348  def details(self):
1349    return self._details
1350
1351  @property
1352  def hal_details(self):
1353    return self._hal_details
1354
1355  @property
1356  def ndk_details(self):
1357    return self._ndk_details
1358
1359  @property
1360  def applied_ndk_details(self):
1361    return (self._details or "") + (self._ndk_details or "")
1362
1363  @property
1364  def tags(self):
1365    if self._tags is None:
1366      return None
1367    else:
1368      return (i for i in self._tags)
1369
1370  @property
1371  def type_notes(self):
1372    return self._type_notes
1373
1374  @property
1375  def typedef(self):
1376    return self._typedef
1377
1378  @property
1379  def enum(self):
1380    return self._enum
1381
1382  @property
1383  def session_characteristics_key_since(self):
1384    return self._session_characteristics_key_since
1385
1386  def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version):
1387    if self._enum is not None:
1388      return self._enum.has_new_values_added_in_hal_version(hal_major_version,hal_minor_version)
1389    else:
1390      return False
1391
1392  def _get_children(self):
1393    if self.enum:
1394      yield self.enum
1395
1396  def sort_children(self):
1397    return None
1398
1399  def is_clone(self):
1400    """
1401    Whether or not this is a Clone instance.
1402
1403    Returns:
1404      False
1405    """
1406    return False
1407
1408  def _init_common(self, **kwargs):
1409
1410    self._parent = None # filled in by Metadata::_construct_entries
1411
1412    self._container = kwargs.get('container')
1413    self._container_sizes = kwargs.get('container_sizes')
1414
1415    hal_version = kwargs.get('hal_version')
1416    if hal_version is None:
1417      if self.is_clone():
1418        self._hal_major_version = 0
1419        self._hal_minor_version = 0
1420      else:
1421        self._hal_major_version = 3
1422        self._hal_minor_version = 2
1423    else:
1424      self._hal_major_version = int(hal_version.partition('.')[0])
1425      self._hal_minor_version = int(hal_version.partition('.')[2])
1426
1427    self._aconfig_flag = kwargs.get('aconfig_flag')
1428
1429    # access these via the 'enum' prop
1430    enum_values = kwargs.get('enum_values')
1431    enum_deprecateds = kwargs.get('enum_deprecateds')
1432    enum_optionals = kwargs.get('enum_optionals')
1433    enum_visibilities = kwargs.get('enum_visibilities')
1434    enum_notes = kwargs.get('enum_notes')  # { value => notes }
1435    enum_sdk_notes = kwargs.get('enum_sdk_notes')  # { value => sdk_notes }
1436    enum_ndk_notes = kwargs.get('enum_ndk_notes')  # { value => ndk_notes }
1437    enum_ids = kwargs.get('enum_ids')  # { value => notes }
1438    enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions }
1439    enum_aconfig_flags = kwargs.get('enum_aconfig_flags') # { value => aconfig flags }
1440
1441    self._tuple_values = kwargs.get('tuple_values')
1442
1443    self._description = kwargs.get('description')
1444    self._range = kwargs.get('range')
1445    self._units = kwargs.get('units')
1446    self._details = kwargs.get('details')
1447    self._hal_details = kwargs.get('hal_details')
1448    self._ndk_details = kwargs.get('ndk_details')
1449
1450    self._tag_ids = kwargs.get('tag_ids', [])
1451    self._tags = None  # Filled in by Metadata::_construct_tags
1452
1453    self._type_notes = kwargs.get('type_notes')
1454    self._type_name = kwargs.get('type_name')
1455    self._typedef = None # Filled in by Metadata::_construct_types
1456
1457    if kwargs.get('enum', False):
1458      self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
1459                        enum_visibilities, enum_notes, enum_sdk_notes, enum_ndk_notes,
1460                        enum_hal_versions, enum_aconfig_flags)
1461    else:
1462      self._enum = None
1463
1464    self._visibility = kwargs.get('visibility')
1465    self._synthetic = kwargs.get('synthetic', False)
1466    self._hwlevel = kwargs.get('hwlevel')
1467    self._deprecated = kwargs.get('deprecated', False)
1468    self._deprecation_description = kwargs.get('deprecation_description')
1469
1470    self._permission_needed = kwargs.get('permission_needed')
1471    self._optional = kwargs.get('optional')
1472    self._ndk_visible = kwargs.get('ndk_visible')
1473
1474    self._session_characteristics_key_since = None \
1475                  if not kwargs.get('session_characteristics_key_since') \
1476                    else int(kwargs.get('session_characteristics_key_since'))
1477
1478    self._property_keys = kwargs
1479
1480  def merge(self):
1481    """
1482    Copy the attributes into a new entry, merging it with the target entry
1483    if it's a clone.
1484    """
1485    return MergedEntry(self)
1486
1487  # Helpers for accessing less than the fully qualified name
1488
1489  def get_name_as_list(self):
1490    """
1491    Returns the name as a list split by a period.
1492
1493    For example:
1494      entry.name is 'android.lens.info.shading'
1495      entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1496    """
1497    return self.name.split(".")
1498
1499  def get_inner_namespace_list(self):
1500    """
1501    Returns the inner namespace part of the name as a list
1502
1503    For example:
1504      entry.name is 'android.lens.info.shading'
1505      entry.get_inner_namespace_list() == ['info']
1506    """
1507    return self.get_name_as_list()[2:-1]
1508
1509  def get_outer_namespace(self):
1510    """
1511    Returns the outer namespace as a string.
1512
1513    For example:
1514      entry.name is 'android.lens.info.shading'
1515      entry.get_outer_namespace() == 'android'
1516
1517    Remarks:
1518      Since outer namespaces are non-recursive,
1519      and each entry has one, this does not need to be a list.
1520    """
1521    return self.get_name_as_list()[0]
1522
1523  def get_section(self):
1524    """
1525    Returns the section as a string.
1526
1527    For example:
1528      entry.name is 'android.lens.info.shading'
1529      entry.get_section() == ''
1530
1531    Remarks:
1532      Since outer namespaces are non-recursive,
1533      and each entry has one, this does not need to be a list.
1534    """
1535    return self.get_name_as_list()[1]
1536
1537  def get_name_minimal(self):
1538    """
1539    Returns only the last component of the fully qualified name as a string.
1540
1541    For example:
1542      entry.name is 'android.lens.info.shading'
1543      entry.get_name_minimal() == 'shading'
1544
1545    Remarks:
1546      entry.name_short it an alias for this
1547    """
1548    return self.get_name_as_list()[-1]
1549
1550  def get_path_without_name(self):
1551    """
1552    Returns a string path to the entry, with the name component excluded.
1553
1554    For example:
1555      entry.name is 'android.lens.info.shading'
1556      entry.get_path_without_name() == 'android.lens.info'
1557    """
1558    return ".".join(self.get_name_as_list()[0:-1])
1559
1560
1561class Clone(Entry):
1562  """
1563  A Node corresponding to a <clone> element. It has all the attributes of an
1564  <entry> element (Entry) plus the additions specified below.
1565
1566  Attributes (Read-Only):
1567    entry: an edge to an Entry object that this targets
1568    target_kind: A string describing the kind of the target entry.
1569    name: a string of the name, same as entry.name
1570    kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1571          for the <clone> element.
1572    type: always None, since a clone cannot override the type.
1573  """
1574  def __init__(self, entry=None, **kwargs):
1575    """
1576    Instantiate a new Clone node.
1577
1578    Args:
1579      name: A string with the fully qualified name, e.g. 'android.shading.mode'
1580      type: A string describing the type, e.g. 'int32'
1581      kind: A string describing the kind, e.g. 'static'
1582      target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1583      hal_version: A string for the initial HIDL HAL metadata version this entry
1584                   was added in
1585
1586    Args (if container):
1587      container: A string describing the container, e.g. 'array' or 'tuple'
1588      container_sizes: A list of string sizes if a container, or None otherwise
1589
1590    Args (if container is 'tuple'):
1591      tuple_values: A list of tuple values, e.g. ['width', 'height']
1592
1593    Args (if the 'enum' attribute is true):
1594      enum: A boolean, True if this is an enum, False otherwise
1595      enum_values: A list of value strings, e.g. ['ON', 'OFF']
1596      enum_optionals: A list of optional enum values, e.g. ['OFF']
1597      enum_notes: A dictionary of value->notes strings.
1598      enum_ids: A dictionary of value->id strings.
1599
1600    Args (optional):
1601      entry: An edge to the corresponding target Entry.
1602      description: A string with a description of the entry.
1603      range: A string with the range of the values of the entry, e.g. '>= 0'
1604      units: A string with the units of the values, e.g. 'inches'
1605      details: A string with the detailed documentation for the entry
1606      hal_details: A string with the HAL implementation details for the entry
1607      ndk_details: A string with the extra NDK documentation for the entry
1608      tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1609      type_notes: A string with the notes for the type
1610
1611    Remarks:
1612      Note that type is not specified since it has to be the same as the
1613      entry.type.
1614    """
1615    self._entry = entry  # Entry object
1616    self._target_kind = kwargs['target_kind']
1617    self._name = kwargs['name']  # same as entry.name
1618    self._kind = kwargs['kind']
1619
1620    # illegal to override the type, it should be the same as the entry
1621    self._type = None
1622    # the rest of the kwargs are optional
1623    # can be used to override the regular entry data
1624    self._init_common(**kwargs)
1625
1626  @property
1627  def entry(self):
1628    return self._entry
1629
1630  @property
1631  def target_kind(self):
1632    return self._target_kind
1633
1634  def is_clone(self):
1635    """
1636    Whether or not this is a Clone instance.
1637
1638    Returns:
1639      True
1640    """
1641    return True
1642
1643class MergedEntry(Entry):
1644  """
1645  A MergedEntry has all the attributes of a Clone and its target Entry merged
1646  together.
1647
1648  Remarks:
1649    Useful when we want to 'unfold' a clone into a real entry by copying out
1650    the target entry data. In this case we don't care about distinguishing
1651    a clone vs an entry.
1652  """
1653  def __init__(self, entry):
1654    """
1655    Create a new instance of MergedEntry.
1656
1657    Args:
1658      entry: An Entry or Clone instance
1659    """
1660    props_distinct = ['description', 'units', 'range', 'details',
1661                      'hal_details', 'ndk_details', 'tags', 'kind',
1662                      'deprecation_description']
1663
1664    for p in props_distinct:
1665      p = '_' + p
1666      if entry.is_clone():
1667        setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1668      else:
1669        setattr(self, p, getattr(entry, p))
1670
1671    props_common = ['parent', 'name', 'container',
1672                    'container_sizes', 'enum',
1673                    'tuple_values',
1674                    'type',
1675                    'type_notes',
1676                    'visibility',
1677                    'ndk_visible',
1678                    'synthetic',
1679                    'hwlevel',
1680                    'deprecated',
1681                    'optional',
1682                    'typedef',
1683                    'hal_major_version',
1684                    'hal_minor_version',
1685                    'permission_needed',
1686                    'aconfig_flag',
1687                    'session_characteristics_key_since'
1688                   ]
1689
1690    for p in props_common:
1691      p = '_' + p
1692      if entry.is_clone():
1693        setattr(self, p, getattr(entry.entry, p))
1694      else:
1695        setattr(self, p, getattr(entry, p))
1696