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_properties.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
355  # After all entries/clones are inserted,
356  # invoke this to generate the parent/child node graph all these objects
357  def construct_graph(self):
358    """
359    Generate the graph recursively, after which all Entry nodes will be
360    accessible recursively by crawling through the outer_namespaces sequence.
361
362    Remarks:
363      This is safe to be called multiple times at any time. It should be done at
364      least once or there will be no graph.
365    """
366    self.validate_tree()
367    self._construct_tags()
368    self.validate_tree()
369    self._construct_types()
370    self.validate_tree()
371    self._construct_clones()
372    self.validate_tree()
373    self._construct_outer_namespaces()
374    self.validate_tree()
375
376  def _construct_tags(self):
377    tag_dict = self._dictionary_by_name(self.tags)
378    for p in self._get_properties():
379      p._tags = []
380      for tag_id in p._tag_ids:
381        tag = tag_dict.get(tag_id)
382
383        if tag not in p._tags:
384          p._tags.append(tag)
385
386        if p not in tag.entries:
387          tag._entries.append(p)
388
389  def _construct_types(self):
390    type_dict = self._dictionary_by_name(self.types)
391    for p in self._get_properties():
392      if p._type_name:
393        type_node = type_dict.get(p._type_name)
394        p._typedef = type_node
395
396        if p not in type_node.entries:
397          type_node._entries.append(p)
398
399  def _construct_clones(self):
400    for p in self._clones:
401      target_kind = p.target_kind
402      target_entry = self._entry_map[target_kind].get(p.name)
403      p._entry = target_entry
404
405      # should not throw if we pass validation
406      # but can happen when importing obsolete CSV entries
407      if target_entry is None:
408        print >> sys.stderr, ("WARNING: Clone entry '%s' target kind '%s'" +   \
409                              " has no corresponding entry")                   \
410                             %(p.name, p.target_kind)
411
412  def _construct_outer_namespaces(self):
413
414    if self._outer_namespaces is None: #the first time this runs
415      self._outer_namespaces = []
416
417    root = self._dictionary_by_name(self._outer_namespaces)
418    for ons_name, ons in root.iteritems():
419      ons._leafs = []
420
421    for p in self._entries_ordered:
422      ons_name = p.get_outer_namespace()
423      ons = root.get(ons_name, OuterNamespace(ons_name, self))
424      root[ons_name] = ons
425
426      if p not in ons._leafs:
427        ons._leafs.append(p)
428
429    for ons_name, ons in root.iteritems():
430
431      ons.validate_tree()
432
433      self._construct_sections(ons)
434
435      if ons not in self._outer_namespaces:
436        self._outer_namespaces.append(ons)
437
438      ons.validate_tree()
439
440  def _construct_sections(self, outer_namespace):
441
442    sections_dict = self._dictionary_by_name(outer_namespace.sections)
443    for sec_name, sec in sections_dict.iteritems():
444      sec._leafs = []
445      sec.validate_tree()
446
447    for p in outer_namespace._leafs:
448      does_exist = sections_dict.get(p.get_section())
449
450      sec = sections_dict.get(p.get_section(), \
451          Section(p.get_section(), outer_namespace))
452      sections_dict[p.get_section()] = sec
453
454      sec.validate_tree()
455
456      if p not in sec._leafs:
457        sec._leafs.append(p)
458
459    for sec_name, sec in sections_dict.iteritems():
460
461      if not sec.validate_tree():
462        print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
463                             "construct_sections (start), with section = '%s'")\
464                             %(sec)
465
466      self._construct_kinds(sec)
467
468      if sec not in outer_namespace.sections:
469        outer_namespace._sections.append(sec)
470
471      if not sec.validate_tree():
472        print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
473                              "construct_sections (end), with section = '%s'") \
474                             %(sec)
475
476  # 'controls', 'static' 'dynamic'. etc
477  def _construct_kinds(self, section):
478    for kind in section.kinds:
479      kind._leafs = []
480      section.validate_tree()
481
482    group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind)
483    leaf_it = ((k, g) for k, g in group_entry_by_kind)
484
485    # allow multiple kinds with the same name. merge if adjacent
486    # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic
487    # this helps maintain ABI compatibility when adding an entry in a new kind
488    for idx, (kind_name, entry_it) in enumerate(leaf_it):
489      if idx >= len(section._kinds):
490        kind = Kind(kind_name, section)
491        section._kinds.append(kind)
492        section.validate_tree()
493
494      kind = section._kinds[idx]
495
496      for p in entry_it:
497        if p not in kind._leafs:
498          kind._leafs.append(p)
499
500    for kind in section._kinds:
501      kind.validate_tree()
502      self._construct_inner_namespaces(kind)
503      kind.validate_tree()
504      self._construct_entries(kind)
505      kind.validate_tree()
506
507      if not section.validate_tree():
508        print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
509                             "construct_kinds, with kind = '%s'") %(kind)
510
511      if not kind.validate_tree():
512        print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
513                              "construct_kinds, with kind = '%s'") %(kind)
514
515  def _construct_inner_namespaces(self, parent, depth=0):
516    #parent is InnerNamespace or Kind
517    ins_dict = self._dictionary_by_name(parent.namespaces)
518    for name, ins in ins_dict.iteritems():
519      ins._leafs = []
520
521    for p in parent._leafs:
522      ins_list = p.get_inner_namespace_list()
523
524      if len(ins_list) > depth:
525        ins_str = ins_list[depth]
526        ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent))
527        ins_dict[ins_str] = ins
528
529        if p not in ins._leafs:
530          ins._leafs.append(p)
531
532    for name, ins in ins_dict.iteritems():
533      ins.validate_tree()
534      # construct children INS
535      self._construct_inner_namespaces(ins, depth + 1)
536      ins.validate_tree()
537      # construct children entries
538      self._construct_entries(ins, depth + 1)
539
540      if ins not in parent.namespaces:
541        parent._namespaces.append(ins)
542
543      if not ins.validate_tree():
544        print >> sys.stderr, ("ERROR: Failed to validate tree in " +           \
545                              "construct_inner_namespaces, with ins = '%s'")   \
546                             %(ins)
547
548  # doesnt construct the entries, so much as links them
549  def _construct_entries(self, parent, depth=0):
550    #parent is InnerNamespace or Kind
551    entry_dict = self._dictionary_by_name(parent.entries)
552    for p in parent._leafs:
553      ins_list = p.get_inner_namespace_list()
554
555      if len(ins_list) == depth:
556        entry = entry_dict.get(p.name, p)
557        entry_dict[p.name] = entry
558
559    for name, entry in entry_dict.iteritems():
560
561      old_parent = entry.parent
562      entry._parent = parent
563
564      if entry not in parent.entries:
565        parent._entries.append(entry)
566
567      if old_parent is not None and old_parent != parent:
568        print >> sys.stderr, ("ERROR: Parent changed from '%s' to '%s' for " + \
569                              "entry '%s'")                                    \
570                             %(old_parent.name, parent.name, entry.name)
571
572  def _get_children(self):
573    if self.outer_namespaces is not None:
574      for i in self.outer_namespaces:
575        yield i
576
577    if self.tags is not None:
578      for i in self.tags:
579        yield i
580
581class Tag(Node):
582  """
583  A tag Node corresponding to a top-level <tag> element.
584
585  Attributes (Read-Only):
586    name: alias for id
587    id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC'
588    description: The description of the tag, the contents of the <tag> element.
589    parent: An edge to the parent, which is always the Metadata root node.
590    entries: A sequence of edges to entries/clones that are using this Tag.
591  """
592  def __init__(self, name, parent, description=""):
593    self._name        = name  # 'id' attribute in XML
594    self._id          = name
595    self._description = description
596    self._parent      = parent
597
598    # all entries that have this tag, including clones
599    self._entries     = []  # filled in by Metadata#construct_tags
600
601  @property
602  def id(self):
603    return self._id
604
605  @property
606  def description(self):
607    return self._description
608
609  @property
610  def entries(self):
611    return (i for i in self._entries)
612
613  def _get_children(self):
614    return None
615
616class Typedef(Node):
617  """
618  A typedef Node corresponding to a <typedef> element under a top-level <types>.
619
620  Attributes (Read-Only):
621    name: The name of this typedef as a string.
622    languages: A dictionary of 'language name' -> 'fully qualified class'.
623    parent: An edge to the parent, which is always the Metadata root node.
624    entries: An iterable over all entries which reference this typedef.
625  """
626  def __init__(self, name, parent, languages=None):
627    self._name        = name
628    self._parent      = parent
629
630    # all entries that have this typedef
631    self._entries     = []  # filled in by Metadata#construct_types
632
633    self._languages   = languages or {}
634
635  @property
636  def languages(self):
637    return self._languages
638
639  @property
640  def entries(self):
641    return (i for i in self._entries)
642
643  def _get_children(self):
644    return None
645
646class OuterNamespace(Node):
647  """
648  A node corresponding to a <namespace> element under <metadata>
649
650  Attributes (Read-Only):
651    name: The name attribute of the <namespace name="foo"> element.
652    parent: An edge to the parent, which is always the Metadata root node.
653    sections: A sequence of Section children.
654  """
655  def __init__(self, name, parent, sections=[]):
656    self._name = name
657    self._parent = parent # MetadataSet
658    self._sections = sections[:]
659    self._leafs = []
660
661    self._children = self._sections
662
663  @property
664  def sections(self):
665    return (i for i in self._sections)
666
667class Section(Node):
668  """
669  A node corresponding to a <section> element under <namespace>
670
671  Attributes (Read-Only):
672    name: The name attribute of the <section name="foo"> element.
673    parent: An edge to the parent, which is always an OuterNamespace instance.
674    description: A string description of the section, or None.
675    kinds: A sequence of Kind children.
676    merged_kinds: A sequence of virtual Kind children,
677                  with each Kind's children merged by the kind.name
678  """
679  def __init__(self, name, parent, description=None, kinds=[]):
680    self._name = name
681    self._parent = parent
682    self._description = description
683    self._kinds = kinds[:]
684
685    self._leafs = []
686
687
688  @property
689  def description(self):
690    return self._description
691
692  @property
693  def kinds(self):
694    return (i for i in self._kinds)
695
696  def sort_children(self):
697    self.validate_tree()
698    # order is always controls,static,dynamic
699    find_child = lambda x: [i for i in self._get_children() if i.name == x]
700    new_lst = find_child('controls') \
701            + find_child('static')   \
702            + find_child('dynamic')
703    self._kinds = new_lst
704    self.validate_tree()
705
706  def _get_children(self):
707    return (i for i in self.kinds)
708
709  @property
710  def merged_kinds(self):
711
712    def aggregate_by_name(acc, el):
713      existing = [i for i in acc if i.name == el.name]
714      if existing:
715        k = existing[0]
716      else:
717        k = Kind(el.name, el.parent)
718        acc.append(k)
719
720      k._namespaces.extend(el._namespaces)
721      k._entries.extend(el._entries)
722
723      return acc
724
725    new_kinds_lst = reduce(aggregate_by_name, self.kinds, [])
726
727    for k in new_kinds_lst:
728      yield k
729
730  def combine_kinds_into_single_node(self):
731    r"""
732    Combines the section's Kinds into a single node.
733
734    Combines all the children (kinds) of this section into a single
735    virtual Kind node.
736
737    Returns:
738      A new Kind node that collapses all Kind siblings into one, combining
739      all their children together.
740
741      For example, given self.kinds == [ x, y ]
742
743        x  y               z
744      / |  | \    -->   / | | \
745      a b  c d          a b c d
746
747      a new instance z is returned in this example.
748
749    Remarks:
750      The children of the kinds are the same references as before, that is
751      their parents will point to the old parents and not to the new parent.
752    """
753    combined = Kind(name="combined", parent=self)
754
755    for k in self._get_children():
756      combined._namespaces.extend(k.namespaces)
757      combined._entries.extend(k.entries)
758
759    return combined
760
761class Kind(Node):
762  """
763  A node corresponding to one of: <static>,<dynamic>,<controls> under a
764  <section> element.
765
766  Attributes (Read-Only):
767    name: A string which is one of 'static', 'dynamic, or 'controls'.
768    parent: An edge to the parent, which is always a Section  instance.
769    namespaces: A sequence of InnerNamespace children.
770    entries: A sequence of Entry/Clone children.
771    merged_entries: A sequence of MergedEntry virtual nodes from entries
772  """
773  def __init__(self, name, parent):
774    self._name = name
775    self._parent = parent
776    self._namespaces = []
777    self._entries = []
778
779    self._leafs = []
780
781  @property
782  def namespaces(self):
783    return self._namespaces
784
785  @property
786  def entries(self):
787    return self._entries
788
789  @property
790  def merged_entries(self):
791    for i in self.entries:
792      yield i.merge()
793
794  def sort_children(self):
795    self._namespaces.sort(key=self._get_name())
796    self._entries.sort(key=self._get_name())
797
798  def _get_children(self):
799    for i in self.namespaces:
800      yield i
801    for i in self.entries:
802      yield i
803
804  def combine_children_by_name(self):
805    r"""
806    Combine multiple children with the same name into a single node.
807
808    Returns:
809      A new Kind where all of the children with the same name were combined.
810
811      For example:
812
813      Given a Kind k:
814
815              k
816            / | \
817            a b c
818            | | |
819            d e f
820
821      a.name == "foo"
822      b.name == "foo"
823      c.name == "bar"
824
825      The returned Kind will look like this:
826
827             k'
828            /  \
829            a' c'
830          / |  |
831          d e  f
832
833    Remarks:
834      This operation is not recursive. To combine the grandchildren and other
835      ancestors, call this method on the ancestor nodes.
836    """
837    return Kind._combine_children_by_name(self, new_type=type(self))
838
839  # new_type is either Kind or InnerNamespace
840  @staticmethod
841  def _combine_children_by_name(self, new_type):
842    new_ins_dict = OrderedDict()
843    new_ent_dict = OrderedDict()
844
845    for ins in self.namespaces:
846      new_ins = new_ins_dict.setdefault(ins.name,
847                                        InnerNamespace(ins.name, parent=self))
848      new_ins._namespaces.extend(ins.namespaces)
849      new_ins._entries.extend(ins.entries)
850
851    for ent in self.entries:
852      new_ent = new_ent_dict.setdefault(ent.name,
853                                        ent.merge())
854
855    kind = new_type(self.name, self.parent)
856    kind._namespaces = new_ins_dict.values()
857    kind._entries = new_ent_dict.values()
858
859    return kind
860
861class InnerNamespace(Node):
862  """
863  A node corresponding to a <namespace> which is an ancestor of a Kind.
864  These namespaces may have other namespaces recursively, or entries as leafs.
865
866  Attributes (Read-Only):
867    name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
868    parent: An edge to the parent, which is an InnerNamespace or a Kind.
869    namespaces: A sequence of InnerNamespace children.
870    entries: A sequence of Entry/Clone children.
871    merged_entries: A sequence of MergedEntry virtual nodes from entries
872  """
873  def __init__(self, name, parent):
874    self._name        = name
875    self._parent      = parent
876    self._namespaces  = []
877    self._entries     = []
878    self._leafs       = []
879
880  @property
881  def namespaces(self):
882    return self._namespaces
883
884  @property
885  def entries(self):
886    return self._entries
887
888  @property
889  def merged_entries(self):
890    for i in self.entries:
891      yield i.merge()
892
893  def sort_children(self):
894    self._namespaces.sort(key=self._get_name())
895    self._entries.sort(key=self._get_name())
896
897  def _get_children(self):
898    for i in self.namespaces:
899      yield i
900    for i in self.entries:
901      yield i
902
903  def combine_children_by_name(self):
904    r"""
905    Combine multiple children with the same name into a single node.
906
907    Returns:
908      A new InnerNamespace where all of the children with the same name were
909      combined.
910
911      For example:
912
913      Given an InnerNamespace i:
914
915              i
916            / | \
917            a b c
918            | | |
919            d e f
920
921      a.name == "foo"
922      b.name == "foo"
923      c.name == "bar"
924
925      The returned InnerNamespace will look like this:
926
927             i'
928            /  \
929            a' c'
930          / |  |
931          d e  f
932
933    Remarks:
934      This operation is not recursive. To combine the grandchildren and other
935      ancestors, call this method on the ancestor nodes.
936    """
937    return Kind._combine_children_by_name(self, new_type=type(self))
938
939class EnumValue(Node):
940  """
941  A class corresponding to a <value> element within an <enum> within an <entry>.
942
943  Attributes (Read-Only):
944    name: A string,                 e.g. 'ON' or 'OFF'
945    id: An optional numeric string, e.g. '0' or '0xFF'
946    deprecated: A boolean, True if the enum should be deprecated.
947    optional: A boolean
948    hidden: A boolean, True if the enum should be hidden.
949    notes: A string describing the notes, or None.
950    parent: An edge to the parent, always an Enum instance.
951  """
952  def __init__(self, name, parent, id=None, deprecated=False, optional=False, hidden=False, notes=None):
953    self._name = name                    # str, e.g. 'ON' or 'OFF'
954    self._id = id                        # int, e.g. '0'
955    self._deprecated = deprecated        # bool
956    self._optional = optional            # bool
957    self._hidden = hidden                # bool
958    self._notes = notes                  # None or str
959    self._parent = parent
960
961  @property
962  def id(self):
963    return self._id
964
965  @property
966  def deprecated(self):
967    return self._deprecated
968
969  @property
970  def optional(self):
971    return self._optional
972
973  @property
974  def hidden(self):
975    return self._hidden
976
977  @property
978  def notes(self):
979    return self._notes
980
981  def _get_children(self):
982    return None
983
984class Enum(Node):
985  """
986  A class corresponding to an <enum> element within an <entry>.
987
988  Attributes (Read-Only):
989    parent: An edge to the parent, always an Entry instance.
990    values: A sequence of EnumValue children.
991    has_values_with_id: A boolean representing if any of the children have a
992        non-empty id property.
993  """
994  def __init__(self, parent, values, ids={}, deprecateds=[], optionals=[], hiddens=[], notes={}):
995    self._values =                                                             \
996      [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens,  \
997                  notes.get(val))                                              \
998        for val in values ]
999
1000    self._parent = parent
1001    self._name = None
1002
1003  @property
1004  def values(self):
1005    return (i for i in self._values)
1006
1007  @property
1008  def has_values_with_id(self):
1009    return bool(any(i for i in self.values if i.id))
1010
1011  def _get_children(self):
1012    return (i for i in self._values)
1013
1014class Entry(Node):
1015  """
1016  A node corresponding to an <entry> element.
1017
1018  Attributes (Read-Only):
1019    parent: An edge to the parent node, which is an InnerNamespace or Kind.
1020    name: The fully qualified name string, e.g. 'android.shading.mode'
1021    name_short: The name attribute from <entry name="mode">, e.g. mode
1022    type: The type attribute from <entry type="bar">
1023    kind: A string ('static', 'dynamic', 'controls') corresponding to the
1024          ancestor Kind#name
1025    container: The container attribute from <entry container="array">, or None.
1026    container_sizes: A sequence of size strings or None if container is None.
1027    enum: An Enum instance if the enum attribute is true, None otherwise.
1028    visibility: The visibility of this entry ('system', 'hidden', 'public')
1029                across the system. System entries are only visible in native code
1030                headers. Hidden entries are marked @hide in managed code, while
1031                public entries are visible in the Android SDK.
1032    applied_visibility: As visibility, but always valid, defaulting to 'system'
1033                        if no visibility is given for an entry.
1034    synthetic: The C-level visibility of this entry ('false', 'true').
1035               Synthetic entries will not be generated into the native metadata
1036               list of entries (in C code). In general a synthetic entry is
1037               glued together at the Java layer from multiple visibiltity=hidden
1038               entries.
1039    hwlevel: The lowest hardware level at which the entry is guaranteed
1040             to be supported by the camera device. All devices with higher
1041             hwlevels will also include this entry. None means that the
1042             entry is optional on any hardware level.
1043    deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1044               unreleased version this needs to be removed altogether. If applied
1045               to an entry from an older release, then this means the entry
1046               should be ignored by newer code.
1047    optional: a bool representing the optional attribute, which denotes the entry
1048              is required for hardware level full devices, but optional for other
1049              hardware levels.  None if not present.
1050    applied_optional: As optional but always valid, defaulting to False if no
1051                      optional attribute is present.
1052    tuple_values: A sequence of strings describing the tuple values,
1053                  None if container is not 'tuple'.
1054    description: A string description, or None.
1055    range: A string range, or None.
1056    units: A string units, or None.
1057    tags: A sequence of Tag nodes associated with this Entry.
1058    type_notes: A string describing notes for the type, or None.
1059    typedef: A Typedef associated with this Entry, or None.
1060
1061  Remarks:
1062    Subclass Clone can be used interchangeable with an Entry,
1063    for when we don't care about the underlying type.
1064
1065    parent and tags edges are invalid until after Metadata#construct_graph
1066    has been invoked.
1067  """
1068  def __init__(self, **kwargs):
1069    """
1070    Instantiate a new Entry node.
1071
1072    Args:
1073      name: A string with the fully qualified name, e.g. 'android.shading.mode'
1074      type: A string describing the type, e.g. 'int32'
1075      kind: A string describing the kind, e.g. 'static'
1076
1077    Args (if container):
1078      container: A string describing the container, e.g. 'array' or 'tuple'
1079      container_sizes: A list of string sizes if a container, or None otherwise
1080
1081    Args (if container is 'tuple'):
1082      tuple_values: A list of tuple values, e.g. ['width', 'height']
1083
1084    Args (if the 'enum' attribute is true):
1085      enum: A boolean, True if this is an enum, False otherwise
1086      enum_values: A list of value strings, e.g. ['ON', 'OFF']
1087      enum_optionals: A list of optional enum values, e.g. ['OFF']
1088      enum_notes: A dictionary of value->notes strings.
1089      enum_ids: A dictionary of value->id strings.
1090
1091    Args (optional):
1092      description: A string with a description of the entry.
1093      range: A string with the range of the values of the entry, e.g. '>= 0'
1094      units: A string with the units of the values, e.g. 'inches'
1095      details: A string with the detailed documentation for the entry
1096      hal_details: A string with the HAL implementation details for the entry
1097      tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1098      type_notes: A string with the notes for the type
1099      visibility: A string describing the visibility, eg 'system', 'hidden',
1100                  'public'
1101      synthetic: A bool to mark whether this entry is visible only at the Java
1102                 layer (True), or at both layers (False = default).
1103      hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
1104      deprecated: A bool to mark whether this is @Deprecated at the Java layer
1105                 (default = False).
1106      optional: A bool to mark whether optional for non-full hardware devices
1107      typedef: A string corresponding to a typedef's name attribute.
1108    """
1109
1110    if kwargs.get('type') is None:
1111      print >> sys.stderr, "ERROR: Missing type for entry '%s' kind  '%s'"     \
1112      %(kwargs.get('name'), kwargs.get('kind'))
1113
1114    # Attributes are Read-Only, but edges may be mutated by
1115    # Metadata, particularly during construct_graph
1116
1117    self._name = kwargs['name']
1118    self._type = kwargs['type']
1119    self._kind = kwargs['kind'] # static, dynamic, or controls
1120
1121    self._init_common(**kwargs)
1122
1123  @property
1124  def type(self):
1125    return self._type
1126
1127  @property
1128  def kind(self):
1129    return self._kind
1130
1131  @property
1132  def visibility(self):
1133    return self._visibility
1134
1135  @property
1136  def applied_visibility(self):
1137    return self._visibility or 'system'
1138
1139  @property
1140  def synthetic(self):
1141    return self._synthetic
1142
1143  @property
1144  def hwlevel(self):
1145    return self._hwlevel
1146
1147  @property
1148  def deprecated(self):
1149    return self._deprecated
1150
1151  # TODO: optional should just return hwlevel is None
1152  @property
1153  def optional(self):
1154    return self._optional
1155
1156  @property
1157  def applied_optional(self):
1158    return self._optional or False
1159
1160  @property
1161  def name_short(self):
1162    return self.get_name_minimal()
1163
1164  @property
1165  def container(self):
1166    return self._container
1167
1168  @property
1169  def container_sizes(self):
1170    if self._container_sizes is None:
1171      return None
1172    else:
1173      return (i for i in self._container_sizes)
1174
1175  @property
1176  def tuple_values(self):
1177    if self._tuple_values is None:
1178      return None
1179    else:
1180      return (i for i in self._tuple_values)
1181
1182  @property
1183  def description(self):
1184    return self._description
1185
1186  @property
1187  def range(self):
1188    return self._range
1189
1190  @property
1191  def units(self):
1192    return self._units
1193
1194  @property
1195  def details(self):
1196    return self._details
1197
1198  @property
1199  def hal_details(self):
1200    return self._hal_details
1201
1202  @property
1203  def tags(self):
1204    if self._tags is None:
1205      return None
1206    else:
1207      return (i for i in self._tags)
1208
1209  @property
1210  def type_notes(self):
1211    return self._type_notes
1212
1213  @property
1214  def typedef(self):
1215    return self._typedef
1216
1217  @property
1218  def enum(self):
1219    return self._enum
1220
1221  def _get_children(self):
1222    if self.enum:
1223      yield self.enum
1224
1225  def sort_children(self):
1226    return None
1227
1228  def is_clone(self):
1229    """
1230    Whether or not this is a Clone instance.
1231
1232    Returns:
1233      False
1234    """
1235    return False
1236
1237  def _init_common(self, **kwargs):
1238
1239    self._parent = None # filled in by Metadata::_construct_entries
1240
1241    self._container = kwargs.get('container')
1242    self._container_sizes = kwargs.get('container_sizes')
1243
1244    # access these via the 'enum' prop
1245    enum_values = kwargs.get('enum_values')
1246    enum_deprecateds = kwargs.get('enum_deprecateds')
1247    enum_optionals = kwargs.get('enum_optionals')
1248    enum_hiddens = kwargs.get('enum_hiddens')
1249    enum_notes = kwargs.get('enum_notes')  # { value => notes }
1250    enum_ids = kwargs.get('enum_ids')  # { value => notes }
1251    self._tuple_values = kwargs.get('tuple_values')
1252
1253    self._description = kwargs.get('description')
1254    self._range = kwargs.get('range')
1255    self._units = kwargs.get('units')
1256    self._details = kwargs.get('details')
1257    self._hal_details = kwargs.get('hal_details')
1258
1259    self._tag_ids = kwargs.get('tag_ids', [])
1260    self._tags = None  # Filled in by Metadata::_construct_tags
1261
1262    self._type_notes = kwargs.get('type_notes')
1263    self._type_name = kwargs.get('type_name')
1264    self._typedef = None # Filled in by Metadata::_construct_types
1265
1266    if kwargs.get('enum', False):
1267      self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
1268                        enum_hiddens, enum_notes)
1269    else:
1270      self._enum = None
1271
1272    self._visibility = kwargs.get('visibility')
1273    self._synthetic = kwargs.get('synthetic', False)
1274    self._hwlevel = kwargs.get('hwlevel')
1275    self._deprecated = kwargs.get('deprecated', False)
1276    self._optional = kwargs.get('optional')
1277
1278    self._property_keys = kwargs
1279
1280  def merge(self):
1281    """
1282    Copy the attributes into a new entry, merging it with the target entry
1283    if it's a clone.
1284    """
1285    return MergedEntry(self)
1286
1287  # Helpers for accessing less than the fully qualified name
1288
1289  def get_name_as_list(self):
1290    """
1291    Returns the name as a list split by a period.
1292
1293    For example:
1294      entry.name is 'android.lens.info.shading'
1295      entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1296    """
1297    return self.name.split(".")
1298
1299  def get_inner_namespace_list(self):
1300    """
1301    Returns the inner namespace part of the name as a list
1302
1303    For example:
1304      entry.name is 'android.lens.info.shading'
1305      entry.get_inner_namespace_list() == ['info']
1306    """
1307    return self.get_name_as_list()[2:-1]
1308
1309  def get_outer_namespace(self):
1310    """
1311    Returns the outer namespace as a string.
1312
1313    For example:
1314      entry.name is 'android.lens.info.shading'
1315      entry.get_outer_namespace() == 'android'
1316
1317    Remarks:
1318      Since outer namespaces are non-recursive,
1319      and each entry has one, this does not need to be a list.
1320    """
1321    return self.get_name_as_list()[0]
1322
1323  def get_section(self):
1324    """
1325    Returns the section as a string.
1326
1327    For example:
1328      entry.name is 'android.lens.info.shading'
1329      entry.get_section() == ''
1330
1331    Remarks:
1332      Since outer namespaces are non-recursive,
1333      and each entry has one, this does not need to be a list.
1334    """
1335    return self.get_name_as_list()[1]
1336
1337  def get_name_minimal(self):
1338    """
1339    Returns only the last component of the fully qualified name as a string.
1340
1341    For example:
1342      entry.name is 'android.lens.info.shading'
1343      entry.get_name_minimal() == 'shading'
1344
1345    Remarks:
1346      entry.name_short it an alias for this
1347    """
1348    return self.get_name_as_list()[-1]
1349
1350  def get_path_without_name(self):
1351    """
1352    Returns a string path to the entry, with the name component excluded.
1353
1354    For example:
1355      entry.name is 'android.lens.info.shading'
1356      entry.get_path_without_name() == 'android.lens.info'
1357    """
1358    return ".".join(self.get_name_as_list()[0:-1])
1359
1360
1361class Clone(Entry):
1362  """
1363  A Node corresponding to a <clone> element. It has all the attributes of an
1364  <entry> element (Entry) plus the additions specified below.
1365
1366  Attributes (Read-Only):
1367    entry: an edge to an Entry object that this targets
1368    target_kind: A string describing the kind of the target entry.
1369    name: a string of the name, same as entry.name
1370    kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1371          for the <clone> element.
1372    type: always None, since a clone cannot override the type.
1373  """
1374  def __init__(self, entry=None, **kwargs):
1375    """
1376    Instantiate a new Clone node.
1377
1378    Args:
1379      name: A string with the fully qualified name, e.g. 'android.shading.mode'
1380      type: A string describing the type, e.g. 'int32'
1381      kind: A string describing the kind, e.g. 'static'
1382      target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1383
1384    Args (if container):
1385      container: A string describing the container, e.g. 'array' or 'tuple'
1386      container_sizes: A list of string sizes if a container, or None otherwise
1387
1388    Args (if container is 'tuple'):
1389      tuple_values: A list of tuple values, e.g. ['width', 'height']
1390
1391    Args (if the 'enum' attribute is true):
1392      enum: A boolean, True if this is an enum, False otherwise
1393      enum_values: A list of value strings, e.g. ['ON', 'OFF']
1394      enum_optionals: A list of optional enum values, e.g. ['OFF']
1395      enum_notes: A dictionary of value->notes strings.
1396      enum_ids: A dictionary of value->id strings.
1397
1398    Args (optional):
1399      entry: An edge to the corresponding target Entry.
1400      description: A string with a description of the entry.
1401      range: A string with the range of the values of the entry, e.g. '>= 0'
1402      units: A string with the units of the values, e.g. 'inches'
1403      details: A string with the detailed documentation for the entry
1404      hal_details: A string with the HAL implementation details for the entry
1405      tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1406      type_notes: A string with the notes for the type
1407
1408    Remarks:
1409      Note that type is not specified since it has to be the same as the
1410      entry.type.
1411    """
1412    self._entry = entry  # Entry object
1413    self._target_kind = kwargs['target_kind']
1414    self._name = kwargs['name']  # same as entry.name
1415    self._kind = kwargs['kind']
1416
1417    # illegal to override the type, it should be the same as the entry
1418    self._type = None
1419    # the rest of the kwargs are optional
1420    # can be used to override the regular entry data
1421    self._init_common(**kwargs)
1422
1423  @property
1424  def entry(self):
1425    return self._entry
1426
1427  @property
1428  def target_kind(self):
1429    return self._target_kind
1430
1431  def is_clone(self):
1432    """
1433    Whether or not this is a Clone instance.
1434
1435    Returns:
1436      True
1437    """
1438    return True
1439
1440class MergedEntry(Entry):
1441  """
1442  A MergedEntry has all the attributes of a Clone and its target Entry merged
1443  together.
1444
1445  Remarks:
1446    Useful when we want to 'unfold' a clone into a real entry by copying out
1447    the target entry data. In this case we don't care about distinguishing
1448    a clone vs an entry.
1449  """
1450  def __init__(self, entry):
1451    """
1452    Create a new instance of MergedEntry.
1453
1454    Args:
1455      entry: An Entry or Clone instance
1456    """
1457    props_distinct = ['description', 'units', 'range', 'details',
1458                      'hal_details', 'tags', 'kind']
1459
1460    for p in props_distinct:
1461      p = '_' + p
1462      if entry.is_clone():
1463        setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1464      else:
1465        setattr(self, p, getattr(entry, p))
1466
1467    props_common = ['parent', 'name', 'container',
1468                    'container_sizes', 'enum',
1469                    'tuple_values',
1470                    'type',
1471                    'type_notes',
1472                    'visibility',
1473                    'synthetic',
1474                    'hwlevel',
1475                    'deprecated',
1476                    'optional',
1477                    'typedef'
1478                   ]
1479
1480    for p in props_common:
1481      p = '_' + p
1482      if entry.is_clone():
1483        setattr(self, p, getattr(entry.entry, p))
1484      else:
1485        setattr(self, p, getattr(entry, p))
1486