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