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