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