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