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_definitions.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 sdk_notes: A string describing extra notes for public SDK only
967 ndk_notes: A string describing extra notes for public NDK only
968 parent: An edge to the parent, always an Enum instance.
970 def __init__(self, name, parent,
971 id=None, deprecated=False, optional=False, hidden=False, notes=None, sdk_notes=None, ndk_notes=None, ndk_hidden=False):
972 self._name = name # str, e.g. 'ON' or 'OFF'
973 self._id = id # int, e.g. '0'
974 self._deprecated = deprecated # bool
975 self._optional = optional # bool
976 self._hidden = hidden # bool
977 self._ndk_hidden = ndk_hidden # bool
978 self._notes = notes # None or str
979 self._sdk_notes = sdk_notes # None or str
980 self._ndk_notes = ndk_notes # None or str
981 self._parent = parent
988 def deprecated(self):
989 return self._deprecated
993 return self._optional
1000 def ndk_hidden(self):
1001 return self._ndk_hidden
1008 def sdk_notes(self):
1009 return self._sdk_notes
1012 def ndk_notes(self):
1013 return self._ndk_notes
1015 def _get_children(self):
1020 A class corresponding to an <enum> element within an <entry>.
1022 Attributes (Read-Only):
1023 parent: An edge to the parent, always an Entry instance.
1024 values: A sequence of EnumValue children.
1025 has_values_with_id: A boolean representing if any of the children have a
1026 non-empty id property.
1028 def __init__(self, parent, values, ids={}, deprecateds=[],
1029 optionals=[], hiddens=[], notes={}, sdk_notes={}, ndk_notes={}, ndk_hiddens=[]):
1031 [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens, \
1032 notes.get(val), sdk_notes.get(val), ndk_notes.get(val), val in ndk_hiddens) \
1035 self._parent = parent
1040 return (i for i in self._values)
1043 def has_values_with_id(self):
1044 return bool(any(i for i in self.values if i.id))
1046 def _get_children(self):
1047 return (i for i in self._values)
1051 A node corresponding to an <entry> element.
1053 Attributes (Read-Only):
1054 parent: An edge to the parent node, which is an InnerNamespace or Kind.
1055 name: The fully qualified name string, e.g. 'android.shading.mode'
1056 name_short: The name attribute from <entry name="mode">, e.g. mode
1057 type: The type attribute from <entry type="bar">
1058 kind: A string ('static', 'dynamic', 'controls') corresponding to the
1060 container: The container attribute from <entry container="array">, or None.
1061 container_sizes: A sequence of size strings or None if container is None.
1062 enum: An Enum instance if the enum attribute is true, None otherwise.
1063 visibility: The visibility of this entry ('system', 'hidden', 'public')
1064 across the system. System entries are only visible in native code
1065 headers. Hidden entries are marked @hide in managed code, while
1066 public entries are visible in the Android SDK.
1067 applied_visibility: As visibility, but always valid, defaulting to 'system'
1068 if no visibility is given for an entry.
1069 applied_ndk_visible: Always valid. Default is 'false'.
1070 Set to 'true' when the visibility implied entry is visible
1072 synthetic: The C-level visibility of this entry ('false', 'true').
1073 Synthetic entries will not be generated into the native metadata
1074 list of entries (in C code). In general a synthetic entry is
1075 glued together at the Java layer from multiple visibiltity=hidden
1077 hwlevel: The lowest hardware level at which the entry is guaranteed
1078 to be supported by the camera device. All devices with higher
1079 hwlevels will also include this entry. None means that the
1080 entry is optional on any hardware level.
1081 deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1082 unreleased version this needs to be removed altogether. If applied
1083 to an entry from an older release, then this means the entry
1084 should be ignored by newer code.
1085 optional: a bool representing the optional attribute, which denotes the entry
1086 is required for hardware level full devices, but optional for other
1087 hardware levels. None if not present.
1088 applied_optional: As optional but always valid, defaulting to False if no
1089 optional attribute is present.
1090 tuple_values: A sequence of strings describing the tuple values,
1091 None if container is not 'tuple'.
1092 description: A string description, or None.
1093 range: A string range, or None.
1094 units: A string units, or None.
1095 tags: A sequence of Tag nodes associated with this Entry.
1096 type_notes: A string describing notes for the type, or None.
1097 typedef: A Typedef associated with this Entry, or None.
1100 Subclass Clone can be used interchangeable with an Entry,
1101 for when we don't care about the underlying type.
1103 parent and tags edges are invalid until after Metadata#construct_graph
1106 def __init__(self, **kwargs):
1108 Instantiate a new Entry node.
1111 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1112 type: A string describing the type, e.g. 'int32'
1113 kind: A string describing the kind, e.g. 'static'
1114 hal_version: A string for the initial HIDL HAL metadata version this entry
1117 Args (if container):
1118 container: A string describing the container, e.g. 'array' or 'tuple'
1119 container_sizes: A list of string sizes if a container, or None otherwise
1121 Args (if container is 'tuple'):
1122 tuple_values: A list of tuple values, e.g. ['width', 'height']
1124 Args (if the 'enum' attribute is true):
1125 enum: A boolean, True if this is an enum, False otherwise
1126 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1127 enum_optionals: A list of optional enum values, e.g. ['OFF']
1128 enum_notes: A dictionary of value->notes strings.
1129 enum_ids: A dictionary of value->id strings.
1132 description: A string with a description of the entry.
1133 range: A string with the range of the values of the entry, e.g. '>= 0'
1134 units: A string with the units of the values, e.g. 'inches'
1135 details: A string with the detailed documentation for the entry
1136 hal_details: A string with the HAL implementation details for the entry
1137 ndk_details: A string with the extra NDK API documentation for the entry=
1138 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1139 type_notes: A string with the notes for the type
1140 visibility: A string describing the visibility, eg 'system', 'hidden',
1142 synthetic: A bool to mark whether this entry is visible only at the Java
1143 layer (True), or at both layers (False = default).
1144 hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
1145 deprecated: A bool to mark whether this is @Deprecated at the Java layer
1147 optional: A bool to mark whether optional for non-full hardware devices
1148 typedef: A string corresponding to a typedef's name attribute.
1151 if kwargs.get('type') is None:
1152 print >> sys.stderr, "ERROR: Missing type for entry '%s' kind '%s'" \
1153 %(kwargs.get('name'), kwargs.get('kind'))
1155 # Attributes are Read-Only, but edges may be mutated by
1156 # Metadata, particularly during construct_graph
1158 self._name = kwargs['name']
1159 self._type = kwargs['type']
1160 self._kind = kwargs['kind'] # static, dynamic, or controls
1162 self._init_common(**kwargs)
1173 def hal_version(self):
1174 return self._hal_version
1177 def visibility(self):
1178 return self._visibility
1181 def applied_visibility(self):
1182 return self._visibility or 'system'
1185 def applied_ndk_visible(self):
1186 if self._visibility in ("public", "ndk_public"):
1191 def synthetic(self):
1192 return self._synthetic
1196 return self._hwlevel
1199 def deprecated(self):
1200 return self._deprecated
1202 # TODO: optional should just return hwlevel is None
1205 return self._optional
1208 def applied_optional(self):
1209 return self._optional or False
1212 def name_short(self):
1213 return self.get_name_minimal()
1216 def container(self):
1217 return self._container
1220 def container_sizes(self):
1221 if self._container_sizes is None:
1224 return (i for i in self._container_sizes)
1227 def tuple_values(self):
1228 if self._tuple_values is None:
1231 return (i for i in self._tuple_values)
1234 def description(self):
1235 return self._description
1247 return self._details
1250 def hal_details(self):
1251 return self._hal_details
1254 def ndk_details(self):
1255 return self._ndk_details
1258 def applied_ndk_details(self):
1259 return (self._details or "") + (self._ndk_details or "")
1263 if self._tags is None:
1266 return (i for i in self._tags)
1269 def type_notes(self):
1270 return self._type_notes
1274 return self._typedef
1280 def _get_children(self):
1284 def sort_children(self):
1289 Whether or not this is a Clone instance.
1296 def _init_common(self, **kwargs):
1298 self._parent = None # filled in by Metadata::_construct_entries
1300 self._container = kwargs.get('container')
1301 self._container_sizes = kwargs.get('container_sizes')
1303 self._hal_version = kwargs.get('hal_version')
1304 if self._hal_version is None:
1305 self._hal_version = '3.2'
1307 # access these via the 'enum' prop
1308 enum_values = kwargs.get('enum_values')
1309 enum_deprecateds = kwargs.get('enum_deprecateds')
1310 enum_optionals = kwargs.get('enum_optionals')
1311 enum_hiddens = kwargs.get('enum_hiddens')
1312 enum_ndk_hiddens = kwargs.get('enum_ndk_hiddens')
1313 enum_notes = kwargs.get('enum_notes') # { value => notes }
1314 enum_sdk_notes = kwargs.get('enum_sdk_notes') # { value => sdk_notes }
1315 enum_ndk_notes = kwargs.get('enum_ndk_notes') # { value => ndk_notes }
1316 enum_ids = kwargs.get('enum_ids') # { value => notes }
1317 self._tuple_values = kwargs.get('tuple_values')
1319 self._description = kwargs.get('description')
1320 self._range = kwargs.get('range')
1321 self._units = kwargs.get('units')
1322 self._details = kwargs.get('details')
1323 self._hal_details = kwargs.get('hal_details')
1324 self._ndk_details = kwargs.get('ndk_details')
1326 self._tag_ids = kwargs.get('tag_ids', [])
1327 self._tags = None # Filled in by Metadata::_construct_tags
1329 self._type_notes = kwargs.get('type_notes')
1330 self._type_name = kwargs.get('type_name')
1331 self._typedef = None # Filled in by Metadata::_construct_types
1333 if kwargs.get('enum', False):
1334 self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
1335 enum_hiddens, enum_notes, enum_sdk_notes, enum_ndk_notes, enum_ndk_hiddens)
1339 self._visibility = kwargs.get('visibility')
1340 self._synthetic = kwargs.get('synthetic', False)
1341 self._hwlevel = kwargs.get('hwlevel')
1342 self._deprecated = kwargs.get('deprecated', False)
1343 self._optional = kwargs.get('optional')
1344 self._ndk_visible = kwargs.get('ndk_visible')
1346 self._property_keys = kwargs
1350 Copy the attributes into a new entry, merging it with the target entry
1353 return MergedEntry(self)
1355 # Helpers for accessing less than the fully qualified name
1357 def get_name_as_list(self):
1359 Returns the name as a list split by a period.
1362 entry.name is 'android.lens.info.shading'
1363 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1365 return self.name.split(".")
1367 def get_inner_namespace_list(self):
1369 Returns the inner namespace part of the name as a list
1372 entry.name is 'android.lens.info.shading'
1373 entry.get_inner_namespace_list() == ['info']
1375 return self.get_name_as_list()[2:-1]
1377 def get_outer_namespace(self):
1379 Returns the outer namespace as a string.
1382 entry.name is 'android.lens.info.shading'
1383 entry.get_outer_namespace() == 'android'
1386 Since outer namespaces are non-recursive,
1387 and each entry has one, this does not need to be a list.
1389 return self.get_name_as_list()[0]
1391 def get_section(self):
1393 Returns the section as a string.
1396 entry.name is 'android.lens.info.shading'
1397 entry.get_section() == ''
1400 Since outer namespaces are non-recursive,
1401 and each entry has one, this does not need to be a list.
1403 return self.get_name_as_list()[1]
1405 def get_name_minimal(self):
1407 Returns only the last component of the fully qualified name as a string.
1410 entry.name is 'android.lens.info.shading'
1411 entry.get_name_minimal() == 'shading'
1414 entry.name_short it an alias for this
1416 return self.get_name_as_list()[-1]
1418 def get_path_without_name(self):
1420 Returns a string path to the entry, with the name component excluded.
1423 entry.name is 'android.lens.info.shading'
1424 entry.get_path_without_name() == 'android.lens.info'
1426 return ".".join(self.get_name_as_list()[0:-1])
1431 A Node corresponding to a <clone> element. It has all the attributes of an
1432 <entry> element (Entry) plus the additions specified below.
1434 Attributes (Read-Only):
1435 entry: an edge to an Entry object that this targets
1436 target_kind: A string describing the kind of the target entry.
1437 name: a string of the name, same as entry.name
1438 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1439 for the <clone> element.
1440 type: always None, since a clone cannot override the type.
1442 def __init__(self, entry=None, **kwargs):
1444 Instantiate a new Clone node.
1447 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1448 type: A string describing the type, e.g. 'int32'
1449 kind: A string describing the kind, e.g. 'static'
1450 target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1451 hal_version: A string for the initial HIDL HAL metadata version this entry
1454 Args (if container):
1455 container: A string describing the container, e.g. 'array' or 'tuple'
1456 container_sizes: A list of string sizes if a container, or None otherwise
1458 Args (if container is 'tuple'):
1459 tuple_values: A list of tuple values, e.g. ['width', 'height']
1461 Args (if the 'enum' attribute is true):
1462 enum: A boolean, True if this is an enum, False otherwise
1463 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1464 enum_optionals: A list of optional enum values, e.g. ['OFF']
1465 enum_notes: A dictionary of value->notes strings.
1466 enum_ids: A dictionary of value->id strings.
1469 entry: An edge to the corresponding target Entry.
1470 description: A string with a description of the entry.
1471 range: A string with the range of the values of the entry, e.g. '>= 0'
1472 units: A string with the units of the values, e.g. 'inches'
1473 details: A string with the detailed documentation for the entry
1474 hal_details: A string with the HAL implementation details for the entry
1475 ndk_details: A string with the extra NDK documentation for the entry
1476 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1477 type_notes: A string with the notes for the type
1480 Note that type is not specified since it has to be the same as the
1483 self._entry = entry # Entry object
1484 self._target_kind = kwargs['target_kind']
1485 self._name = kwargs['name'] # same as entry.name
1486 self._kind = kwargs['kind']
1488 # illegal to override the type, it should be the same as the entry
1490 # the rest of the kwargs are optional
1491 # can be used to override the regular entry data
1492 self._init_common(**kwargs)
1499 def target_kind(self):
1500 return self._target_kind
1504 Whether or not this is a Clone instance.
1511 class MergedEntry(Entry):
1513 A MergedEntry has all the attributes of a Clone and its target Entry merged
1517 Useful when we want to 'unfold' a clone into a real entry by copying out
1518 the target entry data. In this case we don't care about distinguishing
1519 a clone vs an entry.
1521 def __init__(self, entry):
1523 Create a new instance of MergedEntry.
1526 entry: An Entry or Clone instance
1528 props_distinct = ['description', 'units', 'range', 'details',
1529 'hal_details', 'ndk_details', 'tags', 'kind']
1531 for p in props_distinct:
1533 if entry.is_clone():
1534 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1536 setattr(self, p, getattr(entry, p))
1538 props_common = ['parent', 'name', 'container',
1539 'container_sizes', 'enum',
1553 for p in props_common:
1555 if entry.is_clone():
1556 setattr(self, p, getattr(entry.entry, p))
1558 setattr(self, p, getattr(entry, p))