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)
355 # After all entries/clones are inserted,
356 # invoke this to generate the parent/child node graph all these objects
357 def construct_graph(self):
359 Generate the graph recursively, after which all Entry nodes will be
360 accessible recursively by crawling through the outer_namespaces sequence.
363 This is safe to be called multiple times at any time. It should be done at
364 least once or there will be no graph.
367 self._construct_tags()
369 self._construct_types()
371 self._construct_clones()
373 self._construct_outer_namespaces()
376 def _construct_tags(self):
377 tag_dict = self._dictionary_by_name(self.tags)
378 for p in self._get_properties():
380 for tag_id in p._tag_ids:
381 tag = tag_dict.get(tag_id)
383 if tag not in p._tags:
386 if p not in tag.entries:
387 tag._entries.append(p)
389 def _construct_types(self):
390 type_dict = self._dictionary_by_name(self.types)
391 for p in self._get_properties():
393 type_node = type_dict.get(p._type_name)
394 p._typedef = type_node
396 if p not in type_node.entries:
397 type_node._entries.append(p)
399 def _construct_clones(self):
400 for p in self._clones:
401 target_kind = p.target_kind
402 target_entry = self._entry_map[target_kind].get(p.name)
403 p._entry = target_entry
405 # should not throw if we pass validation
406 # but can happen when importing obsolete CSV entries
407 if target_entry is None:
408 print >> sys.stderr, ("WARNING: Clone entry '%s' target kind '%s'" + \
409 " has no corresponding entry") \
410 %(p.name, p.target_kind)
412 def _construct_outer_namespaces(self):
414 if self._outer_namespaces is None: #the first time this runs
415 self._outer_namespaces = []
417 root = self._dictionary_by_name(self._outer_namespaces)
418 for ons_name, ons in root.iteritems():
421 for p in self._entries_ordered:
422 ons_name = p.get_outer_namespace()
423 ons = root.get(ons_name, OuterNamespace(ons_name, self))
426 if p not in ons._leafs:
429 for ons_name, ons in root.iteritems():
433 self._construct_sections(ons)
435 if ons not in self._outer_namespaces:
436 self._outer_namespaces.append(ons)
440 def _construct_sections(self, outer_namespace):
442 sections_dict = self._dictionary_by_name(outer_namespace.sections)
443 for sec_name, sec in sections_dict.iteritems():
447 for p in outer_namespace._leafs:
448 does_exist = sections_dict.get(p.get_section())
450 sec = sections_dict.get(p.get_section(), \
451 Section(p.get_section(), outer_namespace))
452 sections_dict[p.get_section()] = sec
456 if p not in sec._leafs:
459 for sec_name, sec in sections_dict.iteritems():
461 if not sec.validate_tree():
462 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
463 "construct_sections (start), with section = '%s'")\
466 self._construct_kinds(sec)
468 if sec not in outer_namespace.sections:
469 outer_namespace._sections.append(sec)
471 if not sec.validate_tree():
472 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
473 "construct_sections (end), with section = '%s'") \
476 # 'controls', 'static' 'dynamic'. etc
477 def _construct_kinds(self, section):
478 for kind in section.kinds:
480 section.validate_tree()
482 group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind)
483 leaf_it = ((k, g) for k, g in group_entry_by_kind)
485 # allow multiple kinds with the same name. merge if adjacent
486 # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic
487 # this helps maintain ABI compatibility when adding an entry in a new kind
488 for idx, (kind_name, entry_it) in enumerate(leaf_it):
489 if idx >= len(section._kinds):
490 kind = Kind(kind_name, section)
491 section._kinds.append(kind)
492 section.validate_tree()
494 kind = section._kinds[idx]
497 if p not in kind._leafs:
498 kind._leafs.append(p)
500 for kind in section._kinds:
502 self._construct_inner_namespaces(kind)
504 self._construct_entries(kind)
507 if not section.validate_tree():
508 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
509 "construct_kinds, with kind = '%s'") %(kind)
511 if not kind.validate_tree():
512 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
513 "construct_kinds, with kind = '%s'") %(kind)
515 def _construct_inner_namespaces(self, parent, depth=0):
516 #parent is InnerNamespace or Kind
517 ins_dict = self._dictionary_by_name(parent.namespaces)
518 for name, ins in ins_dict.iteritems():
521 for p in parent._leafs:
522 ins_list = p.get_inner_namespace_list()
524 if len(ins_list) > depth:
525 ins_str = ins_list[depth]
526 ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent))
527 ins_dict[ins_str] = ins
529 if p not in ins._leafs:
532 for name, ins in ins_dict.iteritems():
534 # construct children INS
535 self._construct_inner_namespaces(ins, depth + 1)
537 # construct children entries
538 self._construct_entries(ins, depth + 1)
540 if ins not in parent.namespaces:
541 parent._namespaces.append(ins)
543 if not ins.validate_tree():
544 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
545 "construct_inner_namespaces, with ins = '%s'") \
548 # doesnt construct the entries, so much as links them
549 def _construct_entries(self, parent, depth=0):
550 #parent is InnerNamespace or Kind
551 entry_dict = self._dictionary_by_name(parent.entries)
552 for p in parent._leafs:
553 ins_list = p.get_inner_namespace_list()
555 if len(ins_list) == depth:
556 entry = entry_dict.get(p.name, p)
557 entry_dict[p.name] = entry
559 for name, entry in entry_dict.iteritems():
561 old_parent = entry.parent
562 entry._parent = parent
564 if entry not in parent.entries:
565 parent._entries.append(entry)
567 if old_parent is not None and old_parent != parent:
568 print >> sys.stderr, ("ERROR: Parent changed from '%s' to '%s' for " + \
570 %(old_parent.name, parent.name, entry.name)
572 def _get_children(self):
573 if self.outer_namespaces is not None:
574 for i in self.outer_namespaces:
577 if self.tags is not None:
583 A tag Node corresponding to a top-level <tag> element.
585 Attributes (Read-Only):
587 id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC'
588 description: The description of the tag, the contents of the <tag> element.
589 parent: An edge to the parent, which is always the Metadata root node.
590 entries: A sequence of edges to entries/clones that are using this Tag.
592 def __init__(self, name, parent, description=""):
593 self._name = name # 'id' attribute in XML
595 self._description = description
596 self._parent = parent
598 # all entries that have this tag, including clones
599 self._entries = [] # filled in by Metadata#construct_tags
606 def description(self):
607 return self._description
611 return (i for i in self._entries)
613 def _get_children(self):
618 A typedef Node corresponding to a <typedef> element under a top-level <types>.
620 Attributes (Read-Only):
621 name: The name of this typedef as a string.
622 languages: A dictionary of 'language name' -> 'fully qualified class'.
623 parent: An edge to the parent, which is always the Metadata root node.
624 entries: An iterable over all entries which reference this typedef.
626 def __init__(self, name, parent, languages=None):
628 self._parent = parent
630 # all entries that have this typedef
631 self._entries = [] # filled in by Metadata#construct_types
633 self._languages = languages or {}
637 return self._languages
641 return (i for i in self._entries)
643 def _get_children(self):
646 class OuterNamespace(Node):
648 A node corresponding to a <namespace> element under <metadata>
650 Attributes (Read-Only):
651 name: The name attribute of the <namespace name="foo"> element.
652 parent: An edge to the parent, which is always the Metadata root node.
653 sections: A sequence of Section children.
655 def __init__(self, name, parent, sections=[]):
657 self._parent = parent # MetadataSet
658 self._sections = sections[:]
661 self._children = self._sections
665 return (i for i in self._sections)
669 A node corresponding to a <section> element under <namespace>
671 Attributes (Read-Only):
672 name: The name attribute of the <section name="foo"> element.
673 parent: An edge to the parent, which is always an OuterNamespace instance.
674 description: A string description of the section, or None.
675 kinds: A sequence of Kind children.
676 merged_kinds: A sequence of virtual Kind children,
677 with each Kind's children merged by the kind.name
679 def __init__(self, name, parent, description=None, kinds=[]):
681 self._parent = parent
682 self._description = description
683 self._kinds = kinds[:]
689 def description(self):
690 return self._description
694 return (i for i in self._kinds)
696 def sort_children(self):
698 # order is always controls,static,dynamic
699 find_child = lambda x: [i for i in self._get_children() if i.name == x]
700 new_lst = find_child('controls') \
701 + find_child('static') \
702 + find_child('dynamic')
703 self._kinds = new_lst
706 def _get_children(self):
707 return (i for i in self.kinds)
710 def merged_kinds(self):
712 def aggregate_by_name(acc, el):
713 existing = [i for i in acc if i.name == el.name]
717 k = Kind(el.name, el.parent)
720 k._namespaces.extend(el._namespaces)
721 k._entries.extend(el._entries)
725 new_kinds_lst = reduce(aggregate_by_name, self.kinds, [])
727 for k in new_kinds_lst:
730 def combine_kinds_into_single_node(self):
732 Combines the section's Kinds into a single node.
734 Combines all the children (kinds) of this section into a single
738 A new Kind node that collapses all Kind siblings into one, combining
739 all their children together.
741 For example, given self.kinds == [ x, y ]
747 a new instance z is returned in this example.
750 The children of the kinds are the same references as before, that is
751 their parents will point to the old parents and not to the new parent.
753 combined = Kind(name="combined", parent=self)
755 for k in self._get_children():
756 combined._namespaces.extend(k.namespaces)
757 combined._entries.extend(k.entries)
763 A node corresponding to one of: <static>,<dynamic>,<controls> under a
766 Attributes (Read-Only):
767 name: A string which is one of 'static', 'dynamic, or 'controls'.
768 parent: An edge to the parent, which is always a Section instance.
769 namespaces: A sequence of InnerNamespace children.
770 entries: A sequence of Entry/Clone children.
771 merged_entries: A sequence of MergedEntry virtual nodes from entries
773 def __init__(self, name, parent):
775 self._parent = parent
776 self._namespaces = []
782 def namespaces(self):
783 return self._namespaces
790 def merged_entries(self):
791 for i in self.entries:
794 def sort_children(self):
795 self._namespaces.sort(key=self._get_name())
796 self._entries.sort(key=self._get_name())
798 def _get_children(self):
799 for i in self.namespaces:
801 for i in self.entries:
804 def combine_children_by_name(self):
806 Combine multiple children with the same name into a single node.
809 A new Kind where all of the children with the same name were combined.
825 The returned Kind will look like this:
834 This operation is not recursive. To combine the grandchildren and other
835 ancestors, call this method on the ancestor nodes.
837 return Kind._combine_children_by_name(self, new_type=type(self))
839 # new_type is either Kind or InnerNamespace
841 def _combine_children_by_name(self, new_type):
842 new_ins_dict = OrderedDict()
843 new_ent_dict = OrderedDict()
845 for ins in self.namespaces:
846 new_ins = new_ins_dict.setdefault(ins.name,
847 InnerNamespace(ins.name, parent=self))
848 new_ins._namespaces.extend(ins.namespaces)
849 new_ins._entries.extend(ins.entries)
851 for ent in self.entries:
852 new_ent = new_ent_dict.setdefault(ent.name,
855 kind = new_type(self.name, self.parent)
856 kind._namespaces = new_ins_dict.values()
857 kind._entries = new_ent_dict.values()
861 class InnerNamespace(Node):
863 A node corresponding to a <namespace> which is an ancestor of a Kind.
864 These namespaces may have other namespaces recursively, or entries as leafs.
866 Attributes (Read-Only):
867 name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
868 parent: An edge to the parent, which is an InnerNamespace or a Kind.
869 namespaces: A sequence of InnerNamespace children.
870 entries: A sequence of Entry/Clone children.
871 merged_entries: A sequence of MergedEntry virtual nodes from entries
873 def __init__(self, name, parent):
875 self._parent = parent
876 self._namespaces = []
881 def namespaces(self):
882 return self._namespaces
889 def merged_entries(self):
890 for i in self.entries:
893 def sort_children(self):
894 self._namespaces.sort(key=self._get_name())
895 self._entries.sort(key=self._get_name())
897 def _get_children(self):
898 for i in self.namespaces:
900 for i in self.entries:
903 def combine_children_by_name(self):
905 Combine multiple children with the same name into a single node.
908 A new InnerNamespace where all of the children with the same name were
913 Given an InnerNamespace i:
925 The returned InnerNamespace will look like this:
934 This operation is not recursive. To combine the grandchildren and other
935 ancestors, call this method on the ancestor nodes.
937 return Kind._combine_children_by_name(self, new_type=type(self))
939 class EnumValue(Node):
941 A class corresponding to a <value> element within an <enum> within an <entry>.
943 Attributes (Read-Only):
944 name: A string, e.g. 'ON' or 'OFF'
945 id: An optional numeric string, e.g. '0' or '0xFF'
946 deprecated: A boolean, True if the enum should be deprecated.
948 hidden: A boolean, True if the enum should be hidden.
949 notes: A string describing the notes, or None.
950 parent: An edge to the parent, always an Enum instance.
952 def __init__(self, name, parent, id=None, deprecated=False, optional=False, hidden=False, notes=None):
953 self._name = name # str, e.g. 'ON' or 'OFF'
954 self._id = id # int, e.g. '0'
955 self._deprecated = deprecated # bool
956 self._optional = optional # bool
957 self._hidden = hidden # bool
958 self._notes = notes # None or str
959 self._parent = parent
966 def deprecated(self):
967 return self._deprecated
971 return self._optional
981 def _get_children(self):
986 A class corresponding to an <enum> element within an <entry>.
988 Attributes (Read-Only):
989 parent: An edge to the parent, always an Entry instance.
990 values: A sequence of EnumValue children.
991 has_values_with_id: A boolean representing if any of the children have a
992 non-empty id property.
994 def __init__(self, parent, values, ids={}, deprecateds=[], optionals=[], hiddens=[], notes={}):
996 [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens, \
1000 self._parent = parent
1005 return (i for i in self._values)
1008 def has_values_with_id(self):
1009 return bool(any(i for i in self.values if i.id))
1011 def _get_children(self):
1012 return (i for i in self._values)
1016 A node corresponding to an <entry> element.
1018 Attributes (Read-Only):
1019 parent: An edge to the parent node, which is an InnerNamespace or Kind.
1020 name: The fully qualified name string, e.g. 'android.shading.mode'
1021 name_short: The name attribute from <entry name="mode">, e.g. mode
1022 type: The type attribute from <entry type="bar">
1023 kind: A string ('static', 'dynamic', 'controls') corresponding to the
1025 container: The container attribute from <entry container="array">, or None.
1026 container_sizes: A sequence of size strings or None if container is None.
1027 enum: An Enum instance if the enum attribute is true, None otherwise.
1028 visibility: The visibility of this entry ('system', 'hidden', 'public')
1029 across the system. System entries are only visible in native code
1030 headers. Hidden entries are marked @hide in managed code, while
1031 public entries are visible in the Android SDK.
1032 applied_visibility: As visibility, but always valid, defaulting to 'system'
1033 if no visibility is given for an entry.
1034 synthetic: The C-level visibility of this entry ('false', 'true').
1035 Synthetic entries will not be generated into the native metadata
1036 list of entries (in C code). In general a synthetic entry is
1037 glued together at the Java layer from multiple visibiltity=hidden
1039 hwlevel: The lowest hardware level at which the entry is guaranteed
1040 to be supported by the camera device. All devices with higher
1041 hwlevels will also include this entry. None means that the
1042 entry is optional on any hardware level.
1043 deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1044 unreleased version this needs to be removed altogether. If applied
1045 to an entry from an older release, then this means the entry
1046 should be ignored by newer code.
1047 optional: a bool representing the optional attribute, which denotes the entry
1048 is required for hardware level full devices, but optional for other
1049 hardware levels. None if not present.
1050 applied_optional: As optional but always valid, defaulting to False if no
1051 optional attribute is present.
1052 tuple_values: A sequence of strings describing the tuple values,
1053 None if container is not 'tuple'.
1054 description: A string description, or None.
1055 range: A string range, or None.
1056 units: A string units, or None.
1057 tags: A sequence of Tag nodes associated with this Entry.
1058 type_notes: A string describing notes for the type, or None.
1059 typedef: A Typedef associated with this Entry, or None.
1062 Subclass Clone can be used interchangeable with an Entry,
1063 for when we don't care about the underlying type.
1065 parent and tags edges are invalid until after Metadata#construct_graph
1068 def __init__(self, **kwargs):
1070 Instantiate a new Entry node.
1073 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1074 type: A string describing the type, e.g. 'int32'
1075 kind: A string describing the kind, e.g. 'static'
1077 Args (if container):
1078 container: A string describing the container, e.g. 'array' or 'tuple'
1079 container_sizes: A list of string sizes if a container, or None otherwise
1081 Args (if container is 'tuple'):
1082 tuple_values: A list of tuple values, e.g. ['width', 'height']
1084 Args (if the 'enum' attribute is true):
1085 enum: A boolean, True if this is an enum, False otherwise
1086 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1087 enum_optionals: A list of optional enum values, e.g. ['OFF']
1088 enum_notes: A dictionary of value->notes strings.
1089 enum_ids: A dictionary of value->id strings.
1092 description: A string with a description of the entry.
1093 range: A string with the range of the values of the entry, e.g. '>= 0'
1094 units: A string with the units of the values, e.g. 'inches'
1095 details: A string with the detailed documentation for the entry
1096 hal_details: A string with the HAL implementation details for the entry
1097 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1098 type_notes: A string with the notes for the type
1099 visibility: A string describing the visibility, eg 'system', 'hidden',
1101 synthetic: A bool to mark whether this entry is visible only at the Java
1102 layer (True), or at both layers (False = default).
1103 hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
1104 deprecated: A bool to mark whether this is @Deprecated at the Java layer
1106 optional: A bool to mark whether optional for non-full hardware devices
1107 typedef: A string corresponding to a typedef's name attribute.
1110 if kwargs.get('type') is None:
1111 print >> sys.stderr, "ERROR: Missing type for entry '%s' kind '%s'" \
1112 %(kwargs.get('name'), kwargs.get('kind'))
1114 # Attributes are Read-Only, but edges may be mutated by
1115 # Metadata, particularly during construct_graph
1117 self._name = kwargs['name']
1118 self._type = kwargs['type']
1119 self._kind = kwargs['kind'] # static, dynamic, or controls
1121 self._init_common(**kwargs)
1132 def visibility(self):
1133 return self._visibility
1136 def applied_visibility(self):
1137 return self._visibility or 'system'
1140 def synthetic(self):
1141 return self._synthetic
1145 return self._hwlevel
1148 def deprecated(self):
1149 return self._deprecated
1151 # TODO: optional should just return hwlevel is None
1154 return self._optional
1157 def applied_optional(self):
1158 return self._optional or False
1161 def name_short(self):
1162 return self.get_name_minimal()
1165 def container(self):
1166 return self._container
1169 def container_sizes(self):
1170 if self._container_sizes is None:
1173 return (i for i in self._container_sizes)
1176 def tuple_values(self):
1177 if self._tuple_values is None:
1180 return (i for i in self._tuple_values)
1183 def description(self):
1184 return self._description
1196 return self._details
1199 def hal_details(self):
1200 return self._hal_details
1204 if self._tags is None:
1207 return (i for i in self._tags)
1210 def type_notes(self):
1211 return self._type_notes
1215 return self._typedef
1221 def _get_children(self):
1225 def sort_children(self):
1230 Whether or not this is a Clone instance.
1237 def _init_common(self, **kwargs):
1239 self._parent = None # filled in by Metadata::_construct_entries
1241 self._container = kwargs.get('container')
1242 self._container_sizes = kwargs.get('container_sizes')
1244 # access these via the 'enum' prop
1245 enum_values = kwargs.get('enum_values')
1246 enum_deprecateds = kwargs.get('enum_deprecateds')
1247 enum_optionals = kwargs.get('enum_optionals')
1248 enum_hiddens = kwargs.get('enum_hiddens')
1249 enum_notes = kwargs.get('enum_notes') # { value => notes }
1250 enum_ids = kwargs.get('enum_ids') # { value => notes }
1251 self._tuple_values = kwargs.get('tuple_values')
1253 self._description = kwargs.get('description')
1254 self._range = kwargs.get('range')
1255 self._units = kwargs.get('units')
1256 self._details = kwargs.get('details')
1257 self._hal_details = kwargs.get('hal_details')
1259 self._tag_ids = kwargs.get('tag_ids', [])
1260 self._tags = None # Filled in by Metadata::_construct_tags
1262 self._type_notes = kwargs.get('type_notes')
1263 self._type_name = kwargs.get('type_name')
1264 self._typedef = None # Filled in by Metadata::_construct_types
1266 if kwargs.get('enum', False):
1267 self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
1268 enum_hiddens, enum_notes)
1272 self._visibility = kwargs.get('visibility')
1273 self._synthetic = kwargs.get('synthetic', False)
1274 self._hwlevel = kwargs.get('hwlevel')
1275 self._deprecated = kwargs.get('deprecated', False)
1276 self._optional = kwargs.get('optional')
1278 self._property_keys = kwargs
1282 Copy the attributes into a new entry, merging it with the target entry
1285 return MergedEntry(self)
1287 # Helpers for accessing less than the fully qualified name
1289 def get_name_as_list(self):
1291 Returns the name as a list split by a period.
1294 entry.name is 'android.lens.info.shading'
1295 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1297 return self.name.split(".")
1299 def get_inner_namespace_list(self):
1301 Returns the inner namespace part of the name as a list
1304 entry.name is 'android.lens.info.shading'
1305 entry.get_inner_namespace_list() == ['info']
1307 return self.get_name_as_list()[2:-1]
1309 def get_outer_namespace(self):
1311 Returns the outer namespace as a string.
1314 entry.name is 'android.lens.info.shading'
1315 entry.get_outer_namespace() == 'android'
1318 Since outer namespaces are non-recursive,
1319 and each entry has one, this does not need to be a list.
1321 return self.get_name_as_list()[0]
1323 def get_section(self):
1325 Returns the section as a string.
1328 entry.name is 'android.lens.info.shading'
1329 entry.get_section() == ''
1332 Since outer namespaces are non-recursive,
1333 and each entry has one, this does not need to be a list.
1335 return self.get_name_as_list()[1]
1337 def get_name_minimal(self):
1339 Returns only the last component of the fully qualified name as a string.
1342 entry.name is 'android.lens.info.shading'
1343 entry.get_name_minimal() == 'shading'
1346 entry.name_short it an alias for this
1348 return self.get_name_as_list()[-1]
1350 def get_path_without_name(self):
1352 Returns a string path to the entry, with the name component excluded.
1355 entry.name is 'android.lens.info.shading'
1356 entry.get_path_without_name() == 'android.lens.info'
1358 return ".".join(self.get_name_as_list()[0:-1])
1363 A Node corresponding to a <clone> element. It has all the attributes of an
1364 <entry> element (Entry) plus the additions specified below.
1366 Attributes (Read-Only):
1367 entry: an edge to an Entry object that this targets
1368 target_kind: A string describing the kind of the target entry.
1369 name: a string of the name, same as entry.name
1370 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1371 for the <clone> element.
1372 type: always None, since a clone cannot override the type.
1374 def __init__(self, entry=None, **kwargs):
1376 Instantiate a new Clone node.
1379 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1380 type: A string describing the type, e.g. 'int32'
1381 kind: A string describing the kind, e.g. 'static'
1382 target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1384 Args (if container):
1385 container: A string describing the container, e.g. 'array' or 'tuple'
1386 container_sizes: A list of string sizes if a container, or None otherwise
1388 Args (if container is 'tuple'):
1389 tuple_values: A list of tuple values, e.g. ['width', 'height']
1391 Args (if the 'enum' attribute is true):
1392 enum: A boolean, True if this is an enum, False otherwise
1393 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1394 enum_optionals: A list of optional enum values, e.g. ['OFF']
1395 enum_notes: A dictionary of value->notes strings.
1396 enum_ids: A dictionary of value->id strings.
1399 entry: An edge to the corresponding target Entry.
1400 description: A string with a description of the entry.
1401 range: A string with the range of the values of the entry, e.g. '>= 0'
1402 units: A string with the units of the values, e.g. 'inches'
1403 details: A string with the detailed documentation for the entry
1404 hal_details: A string with the HAL implementation details for the entry
1405 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1406 type_notes: A string with the notes for the type
1409 Note that type is not specified since it has to be the same as the
1412 self._entry = entry # Entry object
1413 self._target_kind = kwargs['target_kind']
1414 self._name = kwargs['name'] # same as entry.name
1415 self._kind = kwargs['kind']
1417 # illegal to override the type, it should be the same as the entry
1419 # the rest of the kwargs are optional
1420 # can be used to override the regular entry data
1421 self._init_common(**kwargs)
1428 def target_kind(self):
1429 return self._target_kind
1433 Whether or not this is a Clone instance.
1440 class MergedEntry(Entry):
1442 A MergedEntry has all the attributes of a Clone and its target Entry merged
1446 Useful when we want to 'unfold' a clone into a real entry by copying out
1447 the target entry data. In this case we don't care about distinguishing
1448 a clone vs an entry.
1450 def __init__(self, entry):
1452 Create a new instance of MergedEntry.
1455 entry: An Entry or Clone instance
1457 props_distinct = ['description', 'units', 'range', 'details',
1458 'hal_details', 'tags', 'kind']
1460 for p in props_distinct:
1462 if entry.is_clone():
1463 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1465 setattr(self, p, getattr(entry, p))
1467 props_common = ['parent', 'name', 'container',
1468 'container_sizes', 'enum',
1480 for p in props_common:
1482 if entry.is_clone():
1483 setattr(self, p, getattr(entry.entry, p))
1485 setattr(self, p, getattr(entry, p))