4 # Copyright (C) 2012 The Android Open Source Project
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
10 # http://www.apache.org/licenses/LICENSE-2.0
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.
20 A set of classes (models) each closely representing an XML node in the
21 metadata_properties.xml file.
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>.
40 from collections import OrderedDict
44 Base class for most nodes that are part of the Metadata graph.
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.
64 def find_all(self, pred):
66 Find all descendants that match the predicate.
69 pred: a predicate function that acts as a filter for a Node
72 A sequence of all descendants for which pred(node) is true,
73 in a pre-order visit order.
78 if self._get_children() is None:
81 for i in self._get_children():
82 for j in i.find_all(pred):
85 def find_first(self, pred):
87 Find the first descendant that matches the predicate.
90 pred: a predicate function that acts as a filter for a Node
93 The first Node from find_all(pred), or None if there were no results.
95 for i in self.find_all(pred):
100 def find_parent_first(self, pred):
102 Find the first ancestor that matches the predicate.
105 pred: A predicate function that acts as a filter for a Node
108 The first ancestor closest to the node for which pred(node) is true.
110 for i in self.find_parents(pred):
115 def find_parents(self, pred):
117 Find all ancestors that match the predicate.
120 pred: A predicate function that acts as a filter for a Node
123 A sequence of all ancestors (closest to furthest) from the node,
124 where pred(node) is true.
128 while parent is not None:
131 parent = parent.parent
133 def sort_children(self):
135 Sorts the immediate children in-place.
137 self._sort_by_name(self._children)
139 def _sort_by_name(self, what):
140 what.sort(key=lambda x: x.name)
143 return lambda x: x.name
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)
149 def _children_name_map_matching(self, match=lambda x: True):
151 for i in self._get_children():
157 def _dictionary_by_name(values):
164 def validate_tree(self):
166 Sanity check the tree recursively, ensuring for a node n, all children's
170 True if validation succeeds, False otherwise.
173 children = self._get_children()
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)
184 succ = child.validate_tree() and succ
189 return "<%s name='%s'>" %(self.__class__, self.name)
191 class Metadata(Node):
193 A node corresponding to a <metadata> entry.
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.
204 Initialize with no children. Use insert_* functions and then
205 construct_graph() to build up the Metadata from some source.
209 # kind => { name => entry }
210 self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} }
211 self._entries_ordered = [] # list of ordered Entry/Clone instances
217 self._outer_namespaces = None
222 def outer_namespaces(self):
223 if self._outer_namespaces is None:
226 return (i for i in self._outer_namespaces)
230 return (i for i in self._tags)
234 return (i for i in self._types)
236 def _get_properties(self):
238 for i in self._entries:
241 for i in self._clones:
244 def insert_tag(self, tag, description=""):
246 Insert a tag into the metadata.
249 tag: A string identifier for a tag.
250 description: A string description for a tag.
253 metadata.insert_tag("BC", "Backwards Compatibility for old API")
256 Subsequent calls to insert_tag with the same tag are safe (they will
259 tag_ids = [tg.name for tg in self.tags if tg.name == tag]
261 self._tags.append(Tag(tag, self, description))
263 def insert_type(self, type_name, type_selector="typedef", **kwargs):
265 Insert a type into the metadata.
268 type_name: A type's name
269 type_selector: The selector for the type, e.g. 'typedef'
271 Args (if type_selector == 'typedef'):
272 languages: A map of 'language name' -> 'fully qualified class path'
275 metadata.insert_type('rectangle', 'typedef',
276 { 'java': 'android.graphics.Rect' })
279 Subsequent calls to insert_type with the same type name are safe (they
283 if type_selector != 'typedef':
284 raise ValueError("Unsupported type_selector given " + type_selector)
286 type_names = [tp.name for tp in self.types if tp.name == tp]
288 self._types.append(Typedef(type_name, self, kwargs.get('languages')))
290 def insert_entry(self, entry):
292 Insert an entry into the metadata.
295 entry: A key-value dictionary describing an entry. Refer to
296 Entry#__init__ for the keys required/optional.
299 Subsequent calls to insert_entry with the same entry+kind name are safe
300 (they will be ignored).
303 self._entries.append(e)
304 self._entry_map[e.kind][e.name] = e
305 self._entries_ordered.append(e)
307 def insert_clone(self, clone):
309 Insert a clone into the metadata.
312 clone: A key-value dictionary describing a clone. Refer to
313 Clone#__init__ for the keys required/optional.
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.
320 # figure out corresponding entry later. allow clone insert, entry insert
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)
327 def prune_clones(self):
329 Remove all clones that don't point to an existing entry.
332 This should be called after all insert_entry/insert_clone calls have
336 for p in self._clones:
338 remove_list.append(p)
340 for p in remove_list:
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)
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)
354 def is_entry_this_kind(self, entry, kind):
356 Check if input entry if of input kind
359 entry: an Entry object
360 kind: a string. Possible values are "static", "dynamic", "controls"
363 A boolean indicating whether input entry is of input kind.
365 if kind not in ("static", "dynamic", "controls"):
366 assert(False), "Unknown kind value " + kind
368 return entry.name in self._entry_map[kind]
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):
374 Generate the graph recursively, after which all Entry nodes will be
375 accessible recursively by crawling through the outer_namespaces sequence.
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.
382 self._construct_tags()
384 self._construct_types()
386 self._construct_clones()
388 self._construct_outer_namespaces()
391 def _construct_tags(self):
392 tag_dict = self._dictionary_by_name(self.tags)
393 for p in self._get_properties():
395 for tag_id in p._tag_ids:
396 tag = tag_dict.get(tag_id)
398 if tag not in p._tags:
401 if p not in tag.entries:
402 tag._entries.append(p)
404 def _construct_types(self):
405 type_dict = self._dictionary_by_name(self.types)
406 for p in self._get_properties():
408 type_node = type_dict.get(p._type_name)
409 p._typedef = type_node
411 if p not in type_node.entries:
412 type_node._entries.append(p)
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
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)
427 def _construct_outer_namespaces(self):
429 if self._outer_namespaces is None: #the first time this runs
430 self._outer_namespaces = []
432 root = self._dictionary_by_name(self._outer_namespaces)
433 for ons_name, ons in root.iteritems():
436 for p in self._entries_ordered:
437 ons_name = p.get_outer_namespace()
438 ons = root.get(ons_name, OuterNamespace(ons_name, self))
441 if p not in ons._leafs:
444 for ons_name, ons in root.iteritems():
448 self._construct_sections(ons)
450 if ons not in self._outer_namespaces:
451 self._outer_namespaces.append(ons)
455 def _construct_sections(self, outer_namespace):
457 sections_dict = self._dictionary_by_name(outer_namespace.sections)
458 for sec_name, sec in sections_dict.iteritems():
462 for p in outer_namespace._leafs:
463 does_exist = sections_dict.get(p.get_section())
465 sec = sections_dict.get(p.get_section(), \
466 Section(p.get_section(), outer_namespace))
467 sections_dict[p.get_section()] = sec
471 if p not in sec._leafs:
474 for sec_name, sec in sections_dict.iteritems():
476 if not sec.validate_tree():
477 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
478 "construct_sections (start), with section = '%s'")\
481 self._construct_kinds(sec)
483 if sec not in outer_namespace.sections:
484 outer_namespace._sections.append(sec)
486 if not sec.validate_tree():
487 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
488 "construct_sections (end), with section = '%s'") \
491 # 'controls', 'static' 'dynamic'. etc
492 def _construct_kinds(self, section):
493 for kind in section.kinds:
495 section.validate_tree()
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)
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()
509 kind = section._kinds[idx]
512 if p not in kind._leafs:
513 kind._leafs.append(p)
515 for kind in section._kinds:
517 self._construct_inner_namespaces(kind)
519 self._construct_entries(kind)
522 if not section.validate_tree():
523 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
524 "construct_kinds, with kind = '%s'") %(kind)
526 if not kind.validate_tree():
527 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
528 "construct_kinds, with kind = '%s'") %(kind)
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():
536 for p in parent._leafs:
537 ins_list = p.get_inner_namespace_list()
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
544 if p not in ins._leafs:
547 for name, ins in ins_dict.iteritems():
549 # construct children INS
550 self._construct_inner_namespaces(ins, depth + 1)
552 # construct children entries
553 self._construct_entries(ins, depth + 1)
555 if ins not in parent.namespaces:
556 parent._namespaces.append(ins)
558 if not ins.validate_tree():
559 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
560 "construct_inner_namespaces, with ins = '%s'") \
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()
570 if len(ins_list) == depth:
571 entry = entry_dict.get(p.name, p)
572 entry_dict[p.name] = entry
574 for name, entry in entry_dict.iteritems():
576 old_parent = entry.parent
577 entry._parent = parent
579 if entry not in parent.entries:
580 parent._entries.append(entry)
582 if old_parent is not None and old_parent != parent:
583 print >> sys.stderr, ("ERROR: Parent changed from '%s' to '%s' for " + \
585 %(old_parent.name, parent.name, entry.name)
587 def _get_children(self):
588 if self.outer_namespaces is not None:
589 for i in self.outer_namespaces:
592 if self.tags is not None:
598 A tag Node corresponding to a top-level <tag> element.
600 Attributes (Read-Only):
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.
607 def __init__(self, name, parent, description=""):
608 self._name = name # 'id' attribute in XML
610 self._description = description
611 self._parent = parent
613 # all entries that have this tag, including clones
614 self._entries = [] # filled in by Metadata#construct_tags
621 def description(self):
622 return self._description
626 return (i for i in self._entries)
628 def _get_children(self):
633 A typedef Node corresponding to a <typedef> element under a top-level <types>.
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.
641 def __init__(self, name, parent, languages=None):
643 self._parent = parent
645 # all entries that have this typedef
646 self._entries = [] # filled in by Metadata#construct_types
648 self._languages = languages or {}
652 return self._languages
656 return (i for i in self._entries)
658 def _get_children(self):
661 class OuterNamespace(Node):
663 A node corresponding to a <namespace> element under <metadata>
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.
670 def __init__(self, name, parent, sections=[]):
672 self._parent = parent # MetadataSet
673 self._sections = sections[:]
676 self._children = self._sections
680 return (i for i in self._sections)
684 A node corresponding to a <section> element under <namespace>
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
694 def __init__(self, name, parent, description=None, kinds=[]):
696 self._parent = parent
697 self._description = description
698 self._kinds = kinds[:]
704 def description(self):
705 return self._description
709 return (i for i in self._kinds)
711 def sort_children(self):
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
721 def _get_children(self):
722 return (i for i in self.kinds)
725 def merged_kinds(self):
727 def aggregate_by_name(acc, el):
728 existing = [i for i in acc if i.name == el.name]
732 k = Kind(el.name, el.parent)
735 k._namespaces.extend(el._namespaces)
736 k._entries.extend(el._entries)
740 new_kinds_lst = reduce(aggregate_by_name, self.kinds, [])
742 for k in new_kinds_lst:
745 def combine_kinds_into_single_node(self):
747 Combines the section's Kinds into a single node.
749 Combines all the children (kinds) of this section into a single
753 A new Kind node that collapses all Kind siblings into one, combining
754 all their children together.
756 For example, given self.kinds == [ x, y ]
762 a new instance z is returned in this example.
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.
768 combined = Kind(name="combined", parent=self)
770 for k in self._get_children():
771 combined._namespaces.extend(k.namespaces)
772 combined._entries.extend(k.entries)
778 A node corresponding to one of: <static>,<dynamic>,<controls> under a
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
788 def __init__(self, name, parent):
790 self._parent = parent
791 self._namespaces = []
797 def namespaces(self):
798 return self._namespaces
805 def merged_entries(self):
806 for i in self.entries:
809 def sort_children(self):
810 self._namespaces.sort(key=self._get_name())
811 self._entries.sort(key=self._get_name())
813 def _get_children(self):
814 for i in self.namespaces:
816 for i in self.entries:
819 def combine_children_by_name(self):
821 Combine multiple children with the same name into a single node.
824 A new Kind where all of the children with the same name were combined.
840 The returned Kind will look like this:
849 This operation is not recursive. To combine the grandchildren and other
850 ancestors, call this method on the ancestor nodes.
852 return Kind._combine_children_by_name(self, new_type=type(self))
854 # new_type is either Kind or InnerNamespace
856 def _combine_children_by_name(self, new_type):
857 new_ins_dict = OrderedDict()
858 new_ent_dict = OrderedDict()
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)
866 for ent in self.entries:
867 new_ent = new_ent_dict.setdefault(ent.name,
870 kind = new_type(self.name, self.parent)
871 kind._namespaces = new_ins_dict.values()
872 kind._entries = new_ent_dict.values()
876 class InnerNamespace(Node):
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.
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
888 def __init__(self, name, parent):
890 self._parent = parent
891 self._namespaces = []
896 def namespaces(self):
897 return self._namespaces
904 def merged_entries(self):
905 for i in self.entries:
908 def sort_children(self):
909 self._namespaces.sort(key=self._get_name())
910 self._entries.sort(key=self._get_name())
912 def _get_children(self):
913 for i in self.namespaces:
915 for i in self.entries:
918 def combine_children_by_name(self):
920 Combine multiple children with the same name into a single node.
923 A new InnerNamespace where all of the children with the same name were
928 Given an InnerNamespace i:
940 The returned InnerNamespace will look like this:
949 This operation is not recursive. To combine the grandchildren and other
950 ancestors, call this method on the ancestor nodes.
952 return Kind._combine_children_by_name(self, new_type=type(self))
954 class EnumValue(Node):
956 A class corresponding to a <value> element within an <enum> within an <entry>.
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.
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.
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
984 def deprecated(self):
985 return self._deprecated
989 return self._optional
996 def ndk_hidden(self):
997 return self._ndk_hidden
1003 def _get_children(self):
1008 A class corresponding to an <enum> element within an <entry>.
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.
1016 def __init__(self, parent, values, ids={}, deprecateds=[],
1017 optionals=[], hiddens=[], notes={}, ndk_hiddens=[]):
1019 [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens, \
1020 notes.get(val), val in ndk_hiddens) \
1023 self._parent = parent
1028 return (i for i in self._values)
1031 def has_values_with_id(self):
1032 return bool(any(i for i in self.values if i.id))
1034 def _get_children(self):
1035 return (i for i in self._values)
1039 A node corresponding to an <entry> element.
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
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
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
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.
1088 Subclass Clone can be used interchangeable with an Entry,
1089 for when we don't care about the underlying type.
1091 parent and tags edges are invalid until after Metadata#construct_graph
1094 def __init__(self, **kwargs):
1096 Instantiate a new Entry node.
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'
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
1107 Args (if container is 'tuple'):
1108 tuple_values: A list of tuple values, e.g. ['width', 'height']
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.
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',
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
1132 optional: A bool to mark whether optional for non-full hardware devices
1133 typedef: A string corresponding to a typedef's name attribute.
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'))
1140 # Attributes are Read-Only, but edges may be mutated by
1141 # Metadata, particularly during construct_graph
1143 self._name = kwargs['name']
1144 self._type = kwargs['type']
1145 self._kind = kwargs['kind'] # static, dynamic, or controls
1147 self._init_common(**kwargs)
1158 def visibility(self):
1159 return self._visibility
1162 def applied_visibility(self):
1163 return self._visibility or 'system'
1166 def applied_ndk_visible(self):
1167 if self._visibility in ("public", "ndk_public"):
1172 def synthetic(self):
1173 return self._synthetic
1177 return self._hwlevel
1180 def deprecated(self):
1181 return self._deprecated
1183 # TODO: optional should just return hwlevel is None
1186 return self._optional
1189 def applied_optional(self):
1190 return self._optional or False
1193 def name_short(self):
1194 return self.get_name_minimal()
1197 def container(self):
1198 return self._container
1201 def container_sizes(self):
1202 if self._container_sizes is None:
1205 return (i for i in self._container_sizes)
1208 def tuple_values(self):
1209 if self._tuple_values is None:
1212 return (i for i in self._tuple_values)
1215 def description(self):
1216 return self._description
1228 return self._details
1231 def hal_details(self):
1232 return self._hal_details
1236 if self._tags is None:
1239 return (i for i in self._tags)
1242 def type_notes(self):
1243 return self._type_notes
1247 return self._typedef
1253 def _get_children(self):
1257 def sort_children(self):
1262 Whether or not this is a Clone instance.
1269 def _init_common(self, **kwargs):
1271 self._parent = None # filled in by Metadata::_construct_entries
1273 self._container = kwargs.get('container')
1274 self._container_sizes = kwargs.get('container_sizes')
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')
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')
1292 self._tag_ids = kwargs.get('tag_ids', [])
1293 self._tags = None # Filled in by Metadata::_construct_tags
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
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)
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')
1312 self._property_keys = kwargs
1316 Copy the attributes into a new entry, merging it with the target entry
1319 return MergedEntry(self)
1321 # Helpers for accessing less than the fully qualified name
1323 def get_name_as_list(self):
1325 Returns the name as a list split by a period.
1328 entry.name is 'android.lens.info.shading'
1329 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1331 return self.name.split(".")
1333 def get_inner_namespace_list(self):
1335 Returns the inner namespace part of the name as a list
1338 entry.name is 'android.lens.info.shading'
1339 entry.get_inner_namespace_list() == ['info']
1341 return self.get_name_as_list()[2:-1]
1343 def get_outer_namespace(self):
1345 Returns the outer namespace as a string.
1348 entry.name is 'android.lens.info.shading'
1349 entry.get_outer_namespace() == 'android'
1352 Since outer namespaces are non-recursive,
1353 and each entry has one, this does not need to be a list.
1355 return self.get_name_as_list()[0]
1357 def get_section(self):
1359 Returns the section as a string.
1362 entry.name is 'android.lens.info.shading'
1363 entry.get_section() == ''
1366 Since outer namespaces are non-recursive,
1367 and each entry has one, this does not need to be a list.
1369 return self.get_name_as_list()[1]
1371 def get_name_minimal(self):
1373 Returns only the last component of the fully qualified name as a string.
1376 entry.name is 'android.lens.info.shading'
1377 entry.get_name_minimal() == 'shading'
1380 entry.name_short it an alias for this
1382 return self.get_name_as_list()[-1]
1384 def get_path_without_name(self):
1386 Returns a string path to the entry, with the name component excluded.
1389 entry.name is 'android.lens.info.shading'
1390 entry.get_path_without_name() == 'android.lens.info'
1392 return ".".join(self.get_name_as_list()[0:-1])
1397 A Node corresponding to a <clone> element. It has all the attributes of an
1398 <entry> element (Entry) plus the additions specified below.
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.
1408 def __init__(self, entry=None, **kwargs):
1410 Instantiate a new Clone node.
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'
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
1422 Args (if container is 'tuple'):
1423 tuple_values: A list of tuple values, e.g. ['width', 'height']
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.
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
1443 Note that type is not specified since it has to be the same as the
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']
1451 # illegal to override the type, it should be the same as the entry
1453 # the rest of the kwargs are optional
1454 # can be used to override the regular entry data
1455 self._init_common(**kwargs)
1462 def target_kind(self):
1463 return self._target_kind
1467 Whether or not this is a Clone instance.
1474 class MergedEntry(Entry):
1476 A MergedEntry has all the attributes of a Clone and its target Entry merged
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.
1484 def __init__(self, entry):
1486 Create a new instance of MergedEntry.
1489 entry: An Entry or Clone instance
1491 props_distinct = ['description', 'units', 'range', 'details',
1492 'hal_details', 'tags', 'kind']
1494 for p in props_distinct:
1496 if entry.is_clone():
1497 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1499 setattr(self, p, getattr(entry, p))
1501 props_common = ['parent', 'name', 'container',
1502 'container_sizes', 'enum',
1515 for p in props_common:
1517 if entry.is_clone():
1518 setattr(self, p, getattr(entry.entry, p))
1520 setattr(self, p, getattr(entry, p))