OSDN Git Service

Merge "Fix integer sanitizer in audio_route_update_path."
[android-x86/system-media.git] / camera / docs / metadata_model.py
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 """
20 A set of classes (models) each closely representing an XML node in the
21 metadata_definitions.xml file.
22
23   Node: Base class for most nodes.
24   Entry: A node corresponding to <entry> elements.
25   Clone: A node corresponding to <clone> elements.
26   MergedEntry: A node corresponding to either <entry> or <clone> elements.
27   Kind: A node corresponding to <dynamic>, <static>, <controls> elements.
28   InnerNamespace: A node corresponding to a <namespace> nested under a <kind>.
29   OuterNamespace: A node corresponding to a <namespace> with <kind> children.
30   Section: A node corresponding to a <section> element.
31   Enum: A class corresponding an <enum> element within an <entry>
32   EnumValue: A class corresponding to a <value> element within an Enum
33   Metadata: Root node that also provides tree construction functionality.
34   Tag: A node corresponding to a top level <tag> element.
35   Typedef: A node corresponding to a <typedef> element under <types>.
36 """
37
38 import sys
39 import itertools
40 from collections import OrderedDict
41
42 class 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
191 class 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
596 class 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
631 class 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
661 class 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
682 class 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     hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
694   """
695   def __init__(self, name, parent, description=None, kinds=[]):
696     self._name = name
697     self._parent = parent
698     self._description = description
699     self._kinds = kinds[:]
700
701     self._leafs = []
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   @property
712   def hal_versions(self):
713     hal_versions = set()
714     for i in self._kinds:
715       for entry in i.entries:
716         hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
717       for namespace in i.namespaces:
718         hal_versions.update(namespace.hal_versions)
719     return hal_versions
720
721   def sort_children(self):
722     self.validate_tree()
723     # order is always controls,static,dynamic
724     find_child = lambda x: [i for i in self._get_children() if i.name == x]
725     new_lst = find_child('controls') \
726             + find_child('static')   \
727             + find_child('dynamic')
728     self._kinds = new_lst
729     self.validate_tree()
730
731   def _get_children(self):
732     return (i for i in self.kinds)
733
734   @property
735   def merged_kinds(self):
736
737     def aggregate_by_name(acc, el):
738       existing = [i for i in acc if i.name == el.name]
739       if existing:
740         k = existing[0]
741       else:
742         k = Kind(el.name, el.parent)
743         acc.append(k)
744
745       k._namespaces.extend(el._namespaces)
746       k._entries.extend(el._entries)
747
748       return acc
749
750     new_kinds_lst = reduce(aggregate_by_name, self.kinds, [])
751
752     for k in new_kinds_lst:
753       yield k
754
755   def combine_kinds_into_single_node(self):
756     r"""
757     Combines the section's Kinds into a single node.
758
759     Combines all the children (kinds) of this section into a single
760     virtual Kind node.
761
762     Returns:
763       A new Kind node that collapses all Kind siblings into one, combining
764       all their children together.
765
766       For example, given self.kinds == [ x, y ]
767
768         x  y               z
769       / |  | \    -->   / | | \
770       a b  c d          a b c d
771
772       a new instance z is returned in this example.
773
774     Remarks:
775       The children of the kinds are the same references as before, that is
776       their parents will point to the old parents and not to the new parent.
777     """
778     combined = Kind(name="combined", parent=self)
779
780     for k in self._get_children():
781       combined._namespaces.extend(k.namespaces)
782       combined._entries.extend(k.entries)
783
784     return combined
785
786 class Kind(Node):
787   """
788   A node corresponding to one of: <static>,<dynamic>,<controls> under a
789   <section> element.
790
791   Attributes (Read-Only):
792     name: A string which is one of 'static', 'dynamic, or 'controls'.
793     parent: An edge to the parent, which is always a Section  instance.
794     namespaces: A sequence of InnerNamespace children.
795     entries: A sequence of Entry/Clone children.
796     merged_entries: A sequence of MergedEntry virtual nodes from entries
797   """
798   def __init__(self, name, parent):
799     self._name = name
800     self._parent = parent
801     self._namespaces = []
802     self._entries = []
803
804     self._leafs = []
805
806   @property
807   def namespaces(self):
808     return self._namespaces
809
810   @property
811   def entries(self):
812     return self._entries
813
814   @property
815   def merged_entries(self):
816     for i in self.entries:
817       yield i.merge()
818
819   def sort_children(self):
820     self._namespaces.sort(key=self._get_name())
821     self._entries.sort(key=self._get_name())
822
823   def _get_children(self):
824     for i in self.namespaces:
825       yield i
826     for i in self.entries:
827       yield i
828
829   def combine_children_by_name(self):
830     r"""
831     Combine multiple children with the same name into a single node.
832
833     Returns:
834       A new Kind where all of the children with the same name were combined.
835
836       For example:
837
838       Given a Kind k:
839
840               k
841             / | \
842             a b c
843             | | |
844             d e f
845
846       a.name == "foo"
847       b.name == "foo"
848       c.name == "bar"
849
850       The returned Kind will look like this:
851
852              k'
853             /  \
854             a' c'
855           / |  |
856           d e  f
857
858     Remarks:
859       This operation is not recursive. To combine the grandchildren and other
860       ancestors, call this method on the ancestor nodes.
861     """
862     return Kind._combine_children_by_name(self, new_type=type(self))
863
864   # new_type is either Kind or InnerNamespace
865   @staticmethod
866   def _combine_children_by_name(self, new_type):
867     new_ins_dict = OrderedDict()
868     new_ent_dict = OrderedDict()
869
870     for ins in self.namespaces:
871       new_ins = new_ins_dict.setdefault(ins.name,
872                                         InnerNamespace(ins.name, parent=self))
873       new_ins._namespaces.extend(ins.namespaces)
874       new_ins._entries.extend(ins.entries)
875
876     for ent in self.entries:
877       new_ent = new_ent_dict.setdefault(ent.name,
878                                         ent.merge())
879
880     kind = new_type(self.name, self.parent)
881     kind._namespaces = new_ins_dict.values()
882     kind._entries = new_ent_dict.values()
883
884     return kind
885
886 class InnerNamespace(Node):
887   """
888   A node corresponding to a <namespace> which is an ancestor of a Kind.
889   These namespaces may have other namespaces recursively, or entries as leafs.
890
891   Attributes (Read-Only):
892     name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
893     parent: An edge to the parent, which is an InnerNamespace or a Kind.
894     namespaces: A sequence of InnerNamespace children.
895     entries: A sequence of Entry/Clone children.
896     merged_entries: A sequence of MergedEntry virtual nodes from entries
897     hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
898   """
899   def __init__(self, name, parent):
900     self._name        = name
901     self._parent      = parent
902     self._namespaces  = []
903     self._entries     = []
904     self._leafs       = []
905
906   @property
907   def namespaces(self):
908     return self._namespaces
909
910   @property
911   def entries(self):
912     return self._entries
913
914   @property
915   def hal_versions(self):
916     hal_versions = set()
917     for entry in self.entries:
918       hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
919     for namespace in self.namespaces:
920       hal_versions.update(namespace.hal_versions)
921     return hal_versions
922
923   @property
924   def merged_entries(self):
925     for i in self.entries:
926       yield i.merge()
927
928   def sort_children(self):
929     self._namespaces.sort(key=self._get_name())
930     self._entries.sort(key=self._get_name())
931
932   def _get_children(self):
933     for i in self.namespaces:
934       yield i
935     for i in self.entries:
936       yield i
937
938   def combine_children_by_name(self):
939     r"""
940     Combine multiple children with the same name into a single node.
941
942     Returns:
943       A new InnerNamespace where all of the children with the same name were
944       combined.
945
946       For example:
947
948       Given an InnerNamespace i:
949
950               i
951             / | \
952             a b c
953             | | |
954             d e f
955
956       a.name == "foo"
957       b.name == "foo"
958       c.name == "bar"
959
960       The returned InnerNamespace will look like this:
961
962              i'
963             /  \
964             a' c'
965           / |  |
966           d e  f
967
968     Remarks:
969       This operation is not recursive. To combine the grandchildren and other
970       ancestors, call this method on the ancestor nodes.
971     """
972     return Kind._combine_children_by_name(self, new_type=type(self))
973
974 class EnumValue(Node):
975   """
976   A class corresponding to a <value> element within an <enum> within an <entry>.
977
978   Attributes (Read-Only):
979     name: A string,                 e.g. 'ON' or 'OFF'
980     id: An optional numeric string, e.g. '0' or '0xFF'
981     deprecated: A boolean, True if the enum should be deprecated.
982     optional: A boolean
983     hidden: A boolean, True if the enum should be hidden.
984     ndk_hidden: A boolean, True if the enum should be hidden in NDK
985     notes: A string describing the notes, or None.
986     sdk_notes: A string describing extra notes for public SDK only
987     ndk_notes: A string describing extra notes for public NDK only
988     parent: An edge to the parent, always an Enum instance.
989   """
990   def __init__(self, name, parent,
991       id=None, deprecated=False, optional=False, hidden=False, notes=None, sdk_notes=None, ndk_notes=None, ndk_hidden=False):
992     self._name = name                    # str, e.g. 'ON' or 'OFF'
993     self._id = id                        # int, e.g. '0'
994     self._deprecated = deprecated        # bool
995     self._optional = optional            # bool
996     self._hidden = hidden                # bool
997     self._ndk_hidden = ndk_hidden        # bool
998     self._notes = notes                  # None or str
999     self._sdk_notes = sdk_notes          # None or str
1000     self._ndk_notes = ndk_notes          # None or str
1001     self._parent = parent
1002
1003   @property
1004   def id(self):
1005     return self._id
1006
1007   @property
1008   def deprecated(self):
1009     return self._deprecated
1010
1011   @property
1012   def optional(self):
1013     return self._optional
1014
1015   @property
1016   def hidden(self):
1017     return self._hidden
1018
1019   @property
1020   def ndk_hidden(self):
1021     return self._ndk_hidden
1022
1023   @property
1024   def notes(self):
1025     return self._notes
1026
1027   @property
1028   def sdk_notes(self):
1029     return self._sdk_notes
1030
1031   @property
1032   def ndk_notes(self):
1033     return self._ndk_notes
1034
1035   def _get_children(self):
1036     return None
1037
1038 class Enum(Node):
1039   """
1040   A class corresponding to an <enum> element within an <entry>.
1041
1042   Attributes (Read-Only):
1043     parent: An edge to the parent, always an Entry instance.
1044     values: A sequence of EnumValue children.
1045     has_values_with_id: A boolean representing if any of the children have a
1046         non-empty id property.
1047   """
1048   def __init__(self, parent, values, ids={}, deprecateds=[],
1049       optionals=[], hiddens=[], notes={}, sdk_notes={}, ndk_notes={}, ndk_hiddens=[]):
1050     self._values =                                                             \
1051       [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens,  \
1052                   notes.get(val), sdk_notes.get(val), ndk_notes.get(val), val in ndk_hiddens)     \
1053         for val in values ]
1054
1055     self._parent = parent
1056     self._name = None
1057
1058   @property
1059   def values(self):
1060     return (i for i in self._values)
1061
1062   @property
1063   def has_values_with_id(self):
1064     return bool(any(i for i in self.values if i.id))
1065
1066   def _get_children(self):
1067     return (i for i in self._values)
1068
1069 class Entry(Node):
1070   """
1071   A node corresponding to an <entry> element.
1072
1073   Attributes (Read-Only):
1074     parent: An edge to the parent node, which is an InnerNamespace or Kind.
1075     name: The fully qualified name string, e.g. 'android.shading.mode'
1076     name_short: The name attribute from <entry name="mode">, e.g. mode
1077     type: The type attribute from <entry type="bar">
1078     kind: A string ('static', 'dynamic', 'controls') corresponding to the
1079           ancestor Kind#name
1080     container: The container attribute from <entry container="array">, or None.
1081     container_sizes: A sequence of size strings or None if container is None.
1082     enum: An Enum instance if the enum attribute is true, None otherwise.
1083     visibility: The visibility of this entry ('system', 'hidden', 'public')
1084                 across the system. System entries are only visible in native code
1085                 headers. Hidden entries are marked @hide in managed code, while
1086                 public entries are visible in the Android SDK.
1087     applied_visibility: As visibility, but always valid, defaulting to 'system'
1088                         if no visibility is given for an entry.
1089     applied_ndk_visible: Always valid. Default is 'false'.
1090                          Set to 'true' when the visibility implied entry is visible
1091                          in NDK.
1092     synthetic: The C-level visibility of this entry ('false', 'true').
1093                Synthetic entries will not be generated into the native metadata
1094                list of entries (in C code). In general a synthetic entry is
1095                glued together at the Java layer from multiple visibiltity=hidden
1096                entries.
1097     hwlevel: The lowest hardware level at which the entry is guaranteed
1098              to be supported by the camera device. All devices with higher
1099              hwlevels will also include this entry. None means that the
1100              entry is optional on any hardware level.
1101     deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1102                unreleased version this needs to be removed altogether. If applied
1103                to an entry from an older release, then this means the entry
1104                should be ignored by newer code.
1105     optional: a bool representing the optional attribute, which denotes the entry
1106               is required for hardware level full devices, but optional for other
1107               hardware levels.  None if not present.
1108     applied_optional: As optional but always valid, defaulting to False if no
1109                       optional attribute is present.
1110     tuple_values: A sequence of strings describing the tuple values,
1111                   None if container is not 'tuple'.
1112     description: A string description, or None.
1113     range: A string range, or None.
1114     units: A string units, or None.
1115     tags: A sequence of Tag nodes associated with this Entry.
1116     type_notes: A string describing notes for the type, or None.
1117     typedef: A Typedef associated with this Entry, or None.
1118
1119   Remarks:
1120     Subclass Clone can be used interchangeable with an Entry,
1121     for when we don't care about the underlying type.
1122
1123     parent and tags edges are invalid until after Metadata#construct_graph
1124     has been invoked.
1125   """
1126   def __init__(self, **kwargs):
1127     """
1128     Instantiate a new Entry node.
1129
1130     Args:
1131       name: A string with the fully qualified name, e.g. 'android.shading.mode'
1132       type: A string describing the type, e.g. 'int32'
1133       kind: A string describing the kind, e.g. 'static'
1134       hal_version: A string for the initial HIDL HAL metadata version this entry
1135                    was added in
1136
1137     Args (if container):
1138       container: A string describing the container, e.g. 'array' or 'tuple'
1139       container_sizes: A list of string sizes if a container, or None otherwise
1140
1141     Args (if container is 'tuple'):
1142       tuple_values: A list of tuple values, e.g. ['width', 'height']
1143
1144     Args (if the 'enum' attribute is true):
1145       enum: A boolean, True if this is an enum, False otherwise
1146       enum_values: A list of value strings, e.g. ['ON', 'OFF']
1147       enum_optionals: A list of optional enum values, e.g. ['OFF']
1148       enum_notes: A dictionary of value->notes strings.
1149       enum_ids: A dictionary of value->id strings.
1150
1151     Args (optional):
1152       description: A string with a description of the entry.
1153       range: A string with the range of the values of the entry, e.g. '>= 0'
1154       units: A string with the units of the values, e.g. 'inches'
1155       details: A string with the detailed documentation for the entry
1156       hal_details: A string with the HAL implementation details for the entry
1157       ndk_details: A string with the extra NDK API documentation for the entry=
1158       tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1159       type_notes: A string with the notes for the type
1160       visibility: A string describing the visibility, eg 'system', 'hidden',
1161                   'public'
1162       synthetic: A bool to mark whether this entry is visible only at the Java
1163                  layer (True), or at both layers (False = default).
1164       hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
1165       deprecated: A bool to mark whether this is @Deprecated at the Java layer
1166                  (default = False).
1167       optional: A bool to mark whether optional for non-full hardware devices
1168       typedef: A string corresponding to a typedef's name attribute.
1169     """
1170
1171     if kwargs.get('type') is None:
1172       print >> sys.stderr, "ERROR: Missing type for entry '%s' kind  '%s'"     \
1173       %(kwargs.get('name'), kwargs.get('kind'))
1174
1175     # Attributes are Read-Only, but edges may be mutated by
1176     # Metadata, particularly during construct_graph
1177
1178     self._name = kwargs['name']
1179     self._type = kwargs['type']
1180     self._kind = kwargs['kind'] # static, dynamic, or controls
1181
1182     self._init_common(**kwargs)
1183
1184   @property
1185   def type(self):
1186     return self._type
1187
1188   @property
1189   def kind(self):
1190     return self._kind
1191
1192   @property
1193   def hal_major_version(self):
1194     return self._hal_major_version
1195
1196   @property
1197   def hal_minor_version(self):
1198     return self._hal_minor_version
1199
1200   @property
1201   def visibility(self):
1202     return self._visibility
1203
1204   @property
1205   def applied_visibility(self):
1206     return self._visibility or 'system'
1207
1208   @property
1209   def applied_ndk_visible(self):
1210     if self._visibility in ("public", "ndk_public"):
1211       return "true"
1212     return "false"
1213
1214   @property
1215   def synthetic(self):
1216     return self._synthetic
1217
1218   @property
1219   def hwlevel(self):
1220     return self._hwlevel
1221
1222   @property
1223   def deprecated(self):
1224     return self._deprecated
1225
1226   # TODO: optional should just return hwlevel is None
1227   @property
1228   def optional(self):
1229     return self._optional
1230
1231   @property
1232   def applied_optional(self):
1233     return self._optional or False
1234
1235   @property
1236   def name_short(self):
1237     return self.get_name_minimal()
1238
1239   @property
1240   def container(self):
1241     return self._container
1242
1243   @property
1244   def container_sizes(self):
1245     if self._container_sizes is None:
1246       return None
1247     else:
1248       return (i for i in self._container_sizes)
1249
1250   @property
1251   def tuple_values(self):
1252     if self._tuple_values is None:
1253       return None
1254     else:
1255       return (i for i in self._tuple_values)
1256
1257   @property
1258   def description(self):
1259     return self._description
1260
1261   @property
1262   def range(self):
1263     return self._range
1264
1265   @property
1266   def units(self):
1267     return self._units
1268
1269   @property
1270   def details(self):
1271     return self._details
1272
1273   @property
1274   def hal_details(self):
1275     return self._hal_details
1276
1277   @property
1278   def ndk_details(self):
1279     return self._ndk_details
1280
1281   @property
1282   def applied_ndk_details(self):
1283     return (self._details or "") + (self._ndk_details or "")
1284
1285   @property
1286   def tags(self):
1287     if self._tags is None:
1288       return None
1289     else:
1290       return (i for i in self._tags)
1291
1292   @property
1293   def type_notes(self):
1294     return self._type_notes
1295
1296   @property
1297   def typedef(self):
1298     return self._typedef
1299
1300   @property
1301   def enum(self):
1302     return self._enum
1303
1304   def _get_children(self):
1305     if self.enum:
1306       yield self.enum
1307
1308   def sort_children(self):
1309     return None
1310
1311   def is_clone(self):
1312     """
1313     Whether or not this is a Clone instance.
1314
1315     Returns:
1316       False
1317     """
1318     return False
1319
1320   def _init_common(self, **kwargs):
1321
1322     self._parent = None # filled in by Metadata::_construct_entries
1323
1324     self._container = kwargs.get('container')
1325     self._container_sizes = kwargs.get('container_sizes')
1326
1327     hal_version = kwargs.get('hal_version')
1328     if hal_version is None:
1329       self._hal_major_version = 3
1330       self._hal_minor_version = 2
1331     else:
1332       self._hal_major_version = int(hal_version.partition('.')[0])
1333       self._hal_minor_version = int(hal_version.partition('.')[2])
1334
1335     # access these via the 'enum' prop
1336     enum_values = kwargs.get('enum_values')
1337     enum_deprecateds = kwargs.get('enum_deprecateds')
1338     enum_optionals = kwargs.get('enum_optionals')
1339     enum_hiddens = kwargs.get('enum_hiddens')
1340     enum_ndk_hiddens = kwargs.get('enum_ndk_hiddens')
1341     enum_notes = kwargs.get('enum_notes')  # { value => notes }
1342     enum_sdk_notes = kwargs.get('enum_sdk_notes')  # { value => sdk_notes }
1343     enum_ndk_notes = kwargs.get('enum_ndk_notes')  # { value => ndk_notes }
1344     enum_ids = kwargs.get('enum_ids')  # { value => notes }
1345     self._tuple_values = kwargs.get('tuple_values')
1346
1347     self._description = kwargs.get('description')
1348     self._range = kwargs.get('range')
1349     self._units = kwargs.get('units')
1350     self._details = kwargs.get('details')
1351     self._hal_details = kwargs.get('hal_details')
1352     self._ndk_details = kwargs.get('ndk_details')
1353
1354     self._tag_ids = kwargs.get('tag_ids', [])
1355     self._tags = None  # Filled in by Metadata::_construct_tags
1356
1357     self._type_notes = kwargs.get('type_notes')
1358     self._type_name = kwargs.get('type_name')
1359     self._typedef = None # Filled in by Metadata::_construct_types
1360
1361     if kwargs.get('enum', False):
1362       self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
1363                         enum_hiddens, enum_notes, enum_sdk_notes, enum_ndk_notes, enum_ndk_hiddens)
1364     else:
1365       self._enum = None
1366
1367     self._visibility = kwargs.get('visibility')
1368     self._synthetic = kwargs.get('synthetic', False)
1369     self._hwlevel = kwargs.get('hwlevel')
1370     self._deprecated = kwargs.get('deprecated', False)
1371     self._optional = kwargs.get('optional')
1372     self._ndk_visible = kwargs.get('ndk_visible')
1373
1374     self._property_keys = kwargs
1375
1376   def merge(self):
1377     """
1378     Copy the attributes into a new entry, merging it with the target entry
1379     if it's a clone.
1380     """
1381     return MergedEntry(self)
1382
1383   # Helpers for accessing less than the fully qualified name
1384
1385   def get_name_as_list(self):
1386     """
1387     Returns the name as a list split by a period.
1388
1389     For example:
1390       entry.name is 'android.lens.info.shading'
1391       entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1392     """
1393     return self.name.split(".")
1394
1395   def get_inner_namespace_list(self):
1396     """
1397     Returns the inner namespace part of the name as a list
1398
1399     For example:
1400       entry.name is 'android.lens.info.shading'
1401       entry.get_inner_namespace_list() == ['info']
1402     """
1403     return self.get_name_as_list()[2:-1]
1404
1405   def get_outer_namespace(self):
1406     """
1407     Returns the outer namespace as a string.
1408
1409     For example:
1410       entry.name is 'android.lens.info.shading'
1411       entry.get_outer_namespace() == 'android'
1412
1413     Remarks:
1414       Since outer namespaces are non-recursive,
1415       and each entry has one, this does not need to be a list.
1416     """
1417     return self.get_name_as_list()[0]
1418
1419   def get_section(self):
1420     """
1421     Returns the section as a string.
1422
1423     For example:
1424       entry.name is 'android.lens.info.shading'
1425       entry.get_section() == ''
1426
1427     Remarks:
1428       Since outer namespaces are non-recursive,
1429       and each entry has one, this does not need to be a list.
1430     """
1431     return self.get_name_as_list()[1]
1432
1433   def get_name_minimal(self):
1434     """
1435     Returns only the last component of the fully qualified name as a string.
1436
1437     For example:
1438       entry.name is 'android.lens.info.shading'
1439       entry.get_name_minimal() == 'shading'
1440
1441     Remarks:
1442       entry.name_short it an alias for this
1443     """
1444     return self.get_name_as_list()[-1]
1445
1446   def get_path_without_name(self):
1447     """
1448     Returns a string path to the entry, with the name component excluded.
1449
1450     For example:
1451       entry.name is 'android.lens.info.shading'
1452       entry.get_path_without_name() == 'android.lens.info'
1453     """
1454     return ".".join(self.get_name_as_list()[0:-1])
1455
1456
1457 class Clone(Entry):
1458   """
1459   A Node corresponding to a <clone> element. It has all the attributes of an
1460   <entry> element (Entry) plus the additions specified below.
1461
1462   Attributes (Read-Only):
1463     entry: an edge to an Entry object that this targets
1464     target_kind: A string describing the kind of the target entry.
1465     name: a string of the name, same as entry.name
1466     kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1467           for the <clone> element.
1468     type: always None, since a clone cannot override the type.
1469   """
1470   def __init__(self, entry=None, **kwargs):
1471     """
1472     Instantiate a new Clone node.
1473
1474     Args:
1475       name: A string with the fully qualified name, e.g. 'android.shading.mode'
1476       type: A string describing the type, e.g. 'int32'
1477       kind: A string describing the kind, e.g. 'static'
1478       target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1479       hal_version: A string for the initial HIDL HAL metadata version this entry
1480                    was added in
1481
1482     Args (if container):
1483       container: A string describing the container, e.g. 'array' or 'tuple'
1484       container_sizes: A list of string sizes if a container, or None otherwise
1485
1486     Args (if container is 'tuple'):
1487       tuple_values: A list of tuple values, e.g. ['width', 'height']
1488
1489     Args (if the 'enum' attribute is true):
1490       enum: A boolean, True if this is an enum, False otherwise
1491       enum_values: A list of value strings, e.g. ['ON', 'OFF']
1492       enum_optionals: A list of optional enum values, e.g. ['OFF']
1493       enum_notes: A dictionary of value->notes strings.
1494       enum_ids: A dictionary of value->id strings.
1495
1496     Args (optional):
1497       entry: An edge to the corresponding target Entry.
1498       description: A string with a description of the entry.
1499       range: A string with the range of the values of the entry, e.g. '>= 0'
1500       units: A string with the units of the values, e.g. 'inches'
1501       details: A string with the detailed documentation for the entry
1502       hal_details: A string with the HAL implementation details for the entry
1503       ndk_details: A string with the extra NDK documentation for the entry
1504       tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1505       type_notes: A string with the notes for the type
1506
1507     Remarks:
1508       Note that type is not specified since it has to be the same as the
1509       entry.type.
1510     """
1511     self._entry = entry  # Entry object
1512     self._target_kind = kwargs['target_kind']
1513     self._name = kwargs['name']  # same as entry.name
1514     self._kind = kwargs['kind']
1515
1516     # illegal to override the type, it should be the same as the entry
1517     self._type = None
1518     # the rest of the kwargs are optional
1519     # can be used to override the regular entry data
1520     self._init_common(**kwargs)
1521
1522   @property
1523   def entry(self):
1524     return self._entry
1525
1526   @property
1527   def target_kind(self):
1528     return self._target_kind
1529
1530   def is_clone(self):
1531     """
1532     Whether or not this is a Clone instance.
1533
1534     Returns:
1535       True
1536     """
1537     return True
1538
1539 class MergedEntry(Entry):
1540   """
1541   A MergedEntry has all the attributes of a Clone and its target Entry merged
1542   together.
1543
1544   Remarks:
1545     Useful when we want to 'unfold' a clone into a real entry by copying out
1546     the target entry data. In this case we don't care about distinguishing
1547     a clone vs an entry.
1548   """
1549   def __init__(self, entry):
1550     """
1551     Create a new instance of MergedEntry.
1552
1553     Args:
1554       entry: An Entry or Clone instance
1555     """
1556     props_distinct = ['description', 'units', 'range', 'details',
1557                       'hal_details', 'ndk_details', 'tags', 'kind']
1558
1559     for p in props_distinct:
1560       p = '_' + p
1561       if entry.is_clone():
1562         setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1563       else:
1564         setattr(self, p, getattr(entry, p))
1565
1566     props_common = ['parent', 'name', 'container',
1567                     'container_sizes', 'enum',
1568                     'tuple_values',
1569                     'type',
1570                     'type_notes',
1571                     'visibility',
1572                     'ndk_visible',
1573                     'synthetic',
1574                     'hwlevel',
1575                     'deprecated',
1576                     'optional',
1577                     'typedef',
1578                     'hal_major_version',
1579                     'hal_minor_version'
1580                    ]
1581
1582     for p in props_common:
1583       p = '_' + p
1584       if entry.is_clone():
1585         setattr(self, p, getattr(entry.entry, p))
1586       else:
1587         setattr(self, p, getattr(entry, p))