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'
947 notes: A string describing the notes, or None.
948 parent: An edge to the parent, always an Enum instance.
950 def __init__(self, name, parent, id=None, optional=False, notes=None):
951 self._name = name # str, e.g. 'ON' or 'OFF'
952 self._id = id # int, e.g. '0'
953 self._optional = optional # bool
954 self._notes = notes # None or str
955 self._parent = parent
963 return self._optional
969 def _get_children(self):
974 A class corresponding to an <enum> element within an <entry>.
976 Attributes (Read-Only):
977 parent: An edge to the parent, always an Entry instance.
978 values: A sequence of EnumValue children.
979 has_values_with_id: A boolean representing if any of the children have a
980 non-empty id property.
982 def __init__(self, parent, values, ids={}, optionals=[], notes={}):
984 [ EnumValue(val, self, ids.get(val), val in optionals, notes.get(val)) \
987 self._parent = parent
992 return (i for i in self._values)
995 def has_values_with_id(self):
996 return bool(any(i for i in self.values if i.id))
998 def _get_children(self):
999 return (i for i in self._values)
1003 A node corresponding to an <entry> element.
1005 Attributes (Read-Only):
1006 parent: An edge to the parent node, which is an InnerNamespace or Kind.
1007 name: The fully qualified name string, e.g. 'android.shading.mode'
1008 name_short: The name attribute from <entry name="mode">, e.g. mode
1009 type: The type attribute from <entry type="bar">
1010 kind: A string ('static', 'dynamic', 'controls') corresponding to the
1012 container: The container attribute from <entry container="array">, or None.
1013 container_sizes: A sequence of size strings or None if container is None.
1014 enum: An Enum instance if the enum attribute is true, None otherwise.
1015 visibility: The visibility of this entry ('system', 'hidden', 'public')
1016 across the system. System entries are only visible in native code
1017 headers. Hidden entries are marked @hide in managed code, while
1018 public entries are visible in the Android SDK.
1019 applied_visibility: As visibility, but always valid, defaulting to 'system'
1020 if no visibility is given for an entry.
1021 optional: a bool representing the optional attribute, which denotes the entry
1022 is required for hardware level full devices, but optional for other
1023 hardware levels. None if not present.
1024 applied_optional: As optional but always valid, defaulting to False if no
1025 optional attribute is present.
1026 tuple_values: A sequence of strings describing the tuple values,
1027 None if container is not 'tuple'.
1028 description: A string description, or None.
1029 range: A string range, or None.
1030 units: A string units, or None.
1031 tags: A sequence of Tag nodes associated with this Entry.
1032 type_notes: A string describing notes for the type, or None.
1033 typedef: A Typedef associated with this Entry, or None.
1036 Subclass Clone can be used interchangeable with an Entry,
1037 for when we don't care about the underlying type.
1039 parent and tags edges are invalid until after Metadata#construct_graph
1042 def __init__(self, **kwargs):
1044 Instantiate a new Entry node.
1047 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1048 type: A string describing the type, e.g. 'int32'
1049 kind: A string describing the kind, e.g. 'static'
1051 Args (if container):
1052 container: A string describing the container, e.g. 'array' or 'tuple'
1053 container_sizes: A list of string sizes if a container, or None otherwise
1055 Args (if container is 'tuple'):
1056 tuple_values: A list of tuple values, e.g. ['width', 'height']
1058 Args (if the 'enum' attribute is true):
1059 enum: A boolean, True if this is an enum, False otherwise
1060 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1061 enum_optionals: A list of optional enum values, e.g. ['OFF']
1062 enum_notes: A dictionary of value->notes strings.
1063 enum_ids: A dictionary of value->id strings.
1066 description: A string with a description of the entry.
1067 range: A string with the range of the values of the entry, e.g. '>= 0'
1068 units: A string with the units of the values, e.g. 'inches'
1069 details: A string with the detailed documentation for the entry
1070 hal_details: A string with the HAL implementation details for the entry
1071 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1072 type_notes: A string with the notes for the type
1073 visibility: A string describing the visibility, eg 'system', 'hidden',
1075 optional: A bool to mark whether optional for non-full hardware devices
1076 typedef: A string corresponding to a typedef's name attribute.
1079 if kwargs.get('type') is None:
1080 print >> sys.stderr, "ERROR: Missing type for entry '%s' kind '%s'" \
1081 %(kwargs.get('name'), kwargs.get('kind'))
1083 # Attributes are Read-Only, but edges may be mutated by
1084 # Metadata, particularly during construct_graph
1086 self._name = kwargs['name']
1087 self._type = kwargs['type']
1088 self._kind = kwargs['kind'] # static, dynamic, or controls
1090 self._init_common(**kwargs)
1101 def visibility(self):
1102 return self._visibility
1105 def applied_visibility(self):
1106 return self._visibility or 'system'
1110 return self._optional
1113 def applied_optional(self):
1114 return self._optional or False
1117 def name_short(self):
1118 return self.get_name_minimal()
1121 def container(self):
1122 return self._container
1125 def container_sizes(self):
1126 if self._container_sizes is None:
1129 return (i for i in self._container_sizes)
1132 def tuple_values(self):
1133 if self._tuple_values is None:
1136 return (i for i in self._tuple_values)
1139 def description(self):
1140 return self._description
1152 return self._details
1155 def hal_details(self):
1156 return self._hal_details
1160 if self._tags is None:
1163 return (i for i in self._tags)
1166 def type_notes(self):
1167 return self._type_notes
1171 return self._typedef
1177 def _get_children(self):
1181 def sort_children(self):
1186 Whether or not this is a Clone instance.
1193 def _init_common(self, **kwargs):
1195 self._parent = None # filled in by Metadata::_construct_entries
1197 self._container = kwargs.get('container')
1198 self._container_sizes = kwargs.get('container_sizes')
1200 # access these via the 'enum' prop
1201 enum_values = kwargs.get('enum_values')
1202 enum_optionals = kwargs.get('enum_optionals')
1203 enum_notes = kwargs.get('enum_notes') # { value => notes }
1204 enum_ids = kwargs.get('enum_ids') # { value => notes }
1205 self._tuple_values = kwargs.get('tuple_values')
1207 self._description = kwargs.get('description')
1208 self._range = kwargs.get('range')
1209 self._units = kwargs.get('units')
1210 self._details = kwargs.get('details')
1211 self._hal_details = kwargs.get('hal_details')
1213 self._tag_ids = kwargs.get('tag_ids', [])
1214 self._tags = None # Filled in by Metadata::_construct_tags
1216 self._type_notes = kwargs.get('type_notes')
1217 self._type_name = kwargs.get('type_name')
1218 self._typedef = None # Filled in by Metadata::_construct_types
1220 if kwargs.get('enum', False):
1221 self._enum = Enum(self, enum_values, enum_ids, enum_optionals, enum_notes)
1225 self._visibility = kwargs.get('visibility')
1226 self._optional = kwargs.get('optional')
1228 self._property_keys = kwargs
1232 Copy the attributes into a new entry, merging it with the target entry
1235 return MergedEntry(self)
1237 # Helpers for accessing less than the fully qualified name
1239 def get_name_as_list(self):
1241 Returns the name as a list split by a period.
1244 entry.name is 'android.lens.info.shading'
1245 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1247 return self.name.split(".")
1249 def get_inner_namespace_list(self):
1251 Returns the inner namespace part of the name as a list
1254 entry.name is 'android.lens.info.shading'
1255 entry.get_inner_namespace_list() == ['info']
1257 return self.get_name_as_list()[2:-1]
1259 def get_outer_namespace(self):
1261 Returns the outer namespace as a string.
1264 entry.name is 'android.lens.info.shading'
1265 entry.get_outer_namespace() == 'android'
1268 Since outer namespaces are non-recursive,
1269 and each entry has one, this does not need to be a list.
1271 return self.get_name_as_list()[0]
1273 def get_section(self):
1275 Returns the section as a string.
1278 entry.name is 'android.lens.info.shading'
1279 entry.get_section() == ''
1282 Since outer namespaces are non-recursive,
1283 and each entry has one, this does not need to be a list.
1285 return self.get_name_as_list()[1]
1287 def get_name_minimal(self):
1289 Returns only the last component of the fully qualified name as a string.
1292 entry.name is 'android.lens.info.shading'
1293 entry.get_name_minimal() == 'shading'
1296 entry.name_short it an alias for this
1298 return self.get_name_as_list()[-1]
1300 def get_path_without_name(self):
1302 Returns a string path to the entry, with the name component excluded.
1305 entry.name is 'android.lens.info.shading'
1306 entry.get_path_without_name() == 'android.lens.info'
1308 return ".".join(self.get_name_as_list()[0:-1])
1313 A Node corresponding to a <clone> element. It has all the attributes of an
1314 <entry> element (Entry) plus the additions specified below.
1316 Attributes (Read-Only):
1317 entry: an edge to an Entry object that this targets
1318 target_kind: A string describing the kind of the target entry.
1319 name: a string of the name, same as entry.name
1320 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1321 for the <clone> element.
1322 type: always None, since a clone cannot override the type.
1324 def __init__(self, entry=None, **kwargs):
1326 Instantiate a new Clone node.
1329 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1330 type: A string describing the type, e.g. 'int32'
1331 kind: A string describing the kind, e.g. 'static'
1332 target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1334 Args (if container):
1335 container: A string describing the container, e.g. 'array' or 'tuple'
1336 container_sizes: A list of string sizes if a container, or None otherwise
1338 Args (if container is 'tuple'):
1339 tuple_values: A list of tuple values, e.g. ['width', 'height']
1341 Args (if the 'enum' attribute is true):
1342 enum: A boolean, True if this is an enum, False otherwise
1343 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1344 enum_optionals: A list of optional enum values, e.g. ['OFF']
1345 enum_notes: A dictionary of value->notes strings.
1346 enum_ids: A dictionary of value->id strings.
1349 entry: An edge to the corresponding target Entry.
1350 description: A string with a description of the entry.
1351 range: A string with the range of the values of the entry, e.g. '>= 0'
1352 units: A string with the units of the values, e.g. 'inches'
1353 details: A string with the detailed documentation for the entry
1354 hal_details: A string with the HAL implementation details for the entry
1355 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1356 type_notes: A string with the notes for the type
1359 Note that type is not specified since it has to be the same as the
1362 self._entry = entry # Entry object
1363 self._target_kind = kwargs['target_kind']
1364 self._name = kwargs['name'] # same as entry.name
1365 self._kind = kwargs['kind']
1367 # illegal to override the type, it should be the same as the entry
1369 # the rest of the kwargs are optional
1370 # can be used to override the regular entry data
1371 self._init_common(**kwargs)
1378 def target_kind(self):
1379 return self._target_kind
1383 Whether or not this is a Clone instance.
1390 class MergedEntry(Entry):
1392 A MergedEntry has all the attributes of a Clone and its target Entry merged
1396 Useful when we want to 'unfold' a clone into a real entry by copying out
1397 the target entry data. In this case we don't care about distinguishing
1398 a clone vs an entry.
1400 def __init__(self, entry):
1402 Create a new instance of MergedEntry.
1405 entry: An Entry or Clone instance
1407 props_distinct = ['description', 'units', 'range', 'details',
1408 'hal_details', 'tags', 'kind']
1410 for p in props_distinct:
1412 if entry.is_clone():
1413 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
1415 setattr(self, p, getattr(entry, p))
1417 props_common = ['parent', 'name', 'container',
1418 'container_sizes', 'enum',
1427 for p in props_common:
1429 if entry.is_clone():
1430 setattr(self, p, getattr(entry.entry, p))
1432 setattr(self, p, getattr(entry, p))