blob: 03dacad76b2be3c5511e457e66441f57a741435b [file] [log] [blame]
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001#!/usr/bin/python
2
3#
4# Copyright (C) 2012 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19"""
20A set of classes (models) each closely representing an XML node in the
21metadata_properties.xml file.
22
23 Node: Base class for most nodes.
24 Entry: A node corresponding to <entry> elements.
25 Clone: A node corresponding to <clone> elements.
26 Kind: A node corresponding to <dynamic>, <static>, <controls> elements.
27 InnerNamespace: A node corresponding to a <namespace> nested under a <kind>.
28 OuterNamespace: A node corresponding to a <namespace> with <kind> children.
29 Section: A node corresponding to a <section> element.
30 Enum: A class corresponding an <enum> element within an <entry>
31 Value: A class corresponding to a <value> element within an Enum
32 Metadata: Root node that also provides tree construction functionality.
33 Tag: A node corresponding to a top level <tag> element.
34"""
35
36import sys
37
38class Node(object):
39 """
40 Base class for most nodes that are part of the Metadata graph.
41
42 Attributes (Read-Only):
43 parent: An edge to a parent Node.
44 name: A string describing the name, usually but not always the 'name'
45 attribute of the corresponding XML node.
46 """
47
48 def __init__(self):
49 self._parent = None
50 self._name = None
51
52 @property
53 def parent(self):
54 return self._parent
55
56 @property
57 def name(self):
58 return self._name
59
60 def find_all(self, pred):
61 """
62 Find all descendants that match the predicate.
63
64 Args:
65 pred: a predicate function that acts as a filter for a Node
66
67 Yields:
68 A sequence of all descendants for which pred(node) is true,
69 in a pre-order visit order.
70 """
71 if pred(self):
72 yield self
73
74 if self._get_children() is None:
75 return
76
77 for i in self._get_children():
78 for j in i.find_all(pred):
79 yield j
80
81
82 def find_first(self, pred):
83 """
84 Find the first descendant that matches the predicate.
85
86 Args:
87 pred: a predicate function that acts as a filter for a Node
88
89 Returns:
90 The first Node from find_all(pred), or None if there were no results.
91 """
92 for i in self.find_all(pred):
93 return i
94
95 return None
96
97 def find_parent_first(self, pred):
98 """
99 Find the first ancestor that matches the predicate.
100
101 Args:
102 pred: A predicate function that acts as a filter for a Node
103
104 Returns:
105 The first ancestor closest to the node for which pred(node) is true.
106 """
107 for i in self.find_parents(pred):
108 return i
109
110 return None
111
112 def find_parents(self, pred):
113 """
114 Find all ancestors that match the predicate.
115
116 Args:
117 pred: A predicate function that acts as a filter for a Node
118
119 Yields:
120 A sequence of all ancestors (closest to furthest) from the node,
121 where pred(node) is true.
122 """
123 parent = self.parent
124
125 while parent is not None:
126 if pred(parent):
127 yield parent
128 parent = parent.parent
129
130 def sort_children(self):
131 """
132 Sorts the immediate children in-place.
133 """
134 self._sort_by_name(self._children)
135
136 def _sort_by_name(self, what):
137 what.sort(key=lambda x: x.name)
138
139 def _get_name(self):
140 return lambda x: x.name
141
142 # Iterate over all children nodes. None when node doesn't support children.
143 def _get_children(self):
144 return (i for i in self._children)
145
146 def _children_name_map_matching(self, match=lambda x: True):
147 d = {}
148 for i in _get_children():
149 if match(i):
150 d[i.name] = i
151 return d
152
153 @staticmethod
154 def _dictionary_by_name(values):
155 d = {}
156 for i in values:
157 d[i.name] = i
158
159 return d
160
161 def validate_tree(self):
162 """
163 Sanity check the tree recursively, ensuring for a node n, all children's
164 parents are also n.
165
166 Returns:
167 True if validation succeeds, False otherwise.
168 """
169 succ = True
170 children = self._get_children()
171 if children is None:
172 return True
173
174 for child in self._get_children():
175 if child.parent != self:
176 print >> sys.stderr, ("ERROR: Node '%s' doesn't match the parent" + \
177 "(expected: %s, actual %s)") \
178 %(child, self, child.parent)
179 succ = False
180
181 succ = child.validate_tree() and succ
182
183 return succ
184
185 def __str__(self):
186 return "<%s name='%s'>" %(self.__class__, self.name)
187
188class Metadata(Node):
189 """
190 A node corresponding to a <metadata> entry.
191
192 Attributes (Read-Only):
193 parent: An edge to the parent Node. This is always None for Metadata.
194 outer_namespaces: A sequence of immediate OuterNamespace children.
195 tags: A sequence of all Tag instances available in the graph.
196 """
197
198 def __init__(self):
199 """
200 Initialize with no children. Use insert_* functions and then
201 construct_graph() to build up the Metadata from some source.
202 """
203
204# Private
205 self._entries = []
206 # kind => { name => entry }
207 self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} }
208 self._clones = []
209
210# Public (Read Only)
211 self._parent = None
212 self._outer_namespaces = None
213 self._tags = []
214
215 @property
216 def outer_namespaces(self):
217 if self._outer_namespaces is None:
218 return None
219 else:
220 return (i for i in self._outer_namespaces)
221
222 @property
223 def tags(self):
224 return (i for i in self._tags)
225
226 def _get_properties(self):
227
228 for i in self._entries:
229 yield i
230
231 for i in self._clones:
232 yield i
233
234 def insert_tag(self, tag, description=""):
235 """
236 Insert a tag into the metadata.
237
238 Args:
239 tag: A string identifier for a tag.
240 description: A string description for a tag.
241
242 Example:
243 metadata.insert_tag("BC", "Backwards Compatibility for old API")
244
245 Remarks:
246 Subsequent calls to insert_tag with the same tag are safe (they will
247 be ignored).
248 """
249 tag_ids = [tg.name for tg in self.tags if tg.name == tag]
250 if not tag_ids:
251 self._tags.append(Tag(tag, self))
252
253 def insert_entry(self, entry):
254 """
255 Insert an entry into the metadata.
256
257 Args:
258 entry: A key-value dictionary describing an entry. Refer to
259 Entry#__init__ for the keys required/optional.
260
261 Remarks:
262 Subsequent calls to insert_entry with the same entry+kind name are safe
263 (they will be ignored).
264 """
265 e = Entry(**entry)
266 self._entries.append(e)
267 self._entry_map[e.kind][e.name] = e
268
269 def insert_clone(self, clone):
270 """
271 Insert a clone into the metadata.
272
273 Args:
274 clone: A key-value dictionary describing a clone. Refer to
275 Clone#__init__ for the keys required/optional.
276
277 Remarks:
278 Subsequent calls to insert_clone with the same clone+kind name are safe
279 (they will be ignored). Also the target entry need not be inserted
280 ahead of the clone entry.
281 """
282 entry_name = clone['name']
283 # figure out corresponding entry later. allow clone insert, entry insert
284 entry = None
285 c = Clone(entry, **clone)
286 self._entry_map[c.kind][c.name] = c
287 self._clones.append(c)
288
289 def prune_clones(self):
290 """
291 Remove all clones that don't point to an existing entry.
292
293 Remarks:
294 This should be called after all insert_entry/insert_clone calls have
295 finished.
296 """
297 remove_list = []
298 for p in self._clones:
299 if p.entry is None:
300 remove_list.append(p)
301
302 for p in remove_list:
303
304 # remove from parent's entries list
305 if p.parent is not None:
306 p.parent._entries.remove(p)
307 # remove from parents' _leafs list
308 for ancestor in p.find_parents(lambda x: not isinstance(x, MetadataSet)):
309 ancestor._leafs.remove(p)
310
311 # remove from global list
312 self._clones.remove(p)
313 self._entry_map[p.kind].pop(p.name)
314
315
316 # After all entries/clones are inserted,
317 # invoke this to generate the parent/child node graph all these objects
318 def construct_graph(self):
319 """
320 Generate the graph recursively, after which all Entry nodes will be
321 accessible recursively by crawling through the outer_namespaces sequence.
322
323 Remarks:
324 This is safe to be called multiple times at any time. It should be done at
325 least once or there will be no graph.
326 """
327 self.validate_tree()
328 self._construct_tags()
329 self.validate_tree()
330 self._construct_clones()
331 self.validate_tree()
332 self._construct_outer_namespaces()
333 self.validate_tree()
334
335 def _construct_tags(self):
336 tag_dict = self._dictionary_by_name(self.tags)
337 for p in self._get_properties():
338 p._tags = []
339 for tag_id in p._tag_ids:
340 tag = tag_dict.get(tag_id)
341
342 if tag not in p._tags:
343 p._tags.append(tag)
344
345 def _construct_clones(self):
346 for p in self._clones:
347 target_kind = p.target_kind
348 target_entry = self._entry_map[target_kind].get(p.name)
349 p._entry = target_entry
350
351 # should not throw if we pass validation
352 # but can happen when importing obsolete CSV entries
353 if target_entry is None:
354 print >> sys.stderr, ("WARNING: Clone entry '%s' target kind '%s'" + \
355 " has no corresponding entry") \
356 %(p.name, p.target_kind)
357
358 def _construct_outer_namespaces(self):
359
360 if self._outer_namespaces is None: #the first time this runs
361 self._outer_namespaces = []
362
363 root = self._dictionary_by_name(self._outer_namespaces)
364 for ons_name, ons in root.iteritems():
365 ons._leafs = []
366
367 for p in self._get_properties():
368 ons_name = p.get_outer_namespace()
369 ons = root.get(ons_name, OuterNamespace(ons_name, self))
370 root[ons_name] = ons
371
372 if p not in ons._leafs:
373 ons._leafs.append(p)
374
375 for ons_name, ons in root.iteritems():
376
377 ons.validate_tree()
378
379 self._construct_sections(ons)
380 ons.sort_children()
381
382 if ons not in self._outer_namespaces:
383 self._outer_namespaces.append(ons)
384
385 ons.validate_tree()
386
387 def _construct_sections(self, outer_namespace):
388
389 sections_dict = self._dictionary_by_name(outer_namespace.sections)
390 for sec_name, sec in sections_dict.iteritems():
391 sec._leafs = []
392 sec.validate_tree()
393
394 for p in outer_namespace._leafs:
395 does_exist = sections_dict.get(p.get_section())
396
397 sec = sections_dict.get(p.get_section(), \
398 Section(p.get_section(), outer_namespace))
399 sections_dict[p.get_section()] = sec
400
401 sec.validate_tree()
402
403 if p not in sec._leafs:
404 sec._leafs.append(p)
405
406 for sec_name, sec in sections_dict.iteritems():
407
408 if not sec.validate_tree():
409 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
410 "construct_sections (start), with section = '%s'")\
411 %(sec)
412
413 self._construct_kinds(sec)
414 sec.sort_children()
415
416 if sec not in outer_namespace.sections:
417 outer_namespace._sections.append(sec)
418
419 if not sec.validate_tree():
420 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
421 "construct_sections (end), with section = '%s'") \
422 %(sec)
423
424 # 'controls', 'static' 'dynamic'. etc
425 def _construct_kinds(self, section):
426
427 kinds_dict = self._dictionary_by_name(section.kinds)
428 for name, kind in kinds_dict.iteritems():
429 kind._leafs = []
430 section.validate_tree()
431
432 for p in section._leafs:
433 kind = kinds_dict.get(p.kind, Kind(p.kind, section))
434 kinds_dict[p.kind] = kind
435 section.validate_tree()
436
437 if p not in kind._leafs:
438 kind._leafs.append(p)
439
440 if len(kinds_dict) > 3:
441 sec = section
442 if sec is not None:
443 sec_name = sec.name
444 else:
445 sec_name = "Unknown"
446
447 print >> sys.stderr, ("ERROR: Kind '%s' has too many children(%d) " + \
448 "in section '%s'") %(name, len(kc), sec_name)
449
450
451 for name, kind in kinds_dict.iteritems():
452
453 kind.validate_tree()
454 self._construct_inner_namespaces(kind)
455 kind.validate_tree()
456 self._construct_entries(kind)
457 kind.sort_children()
458 kind.validate_tree()
459
460 if kind not in section.kinds:
461 section._kinds.append(kind)
462
463 if not section.validate_tree():
464 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
465 "construct_kinds, with kind = '%s'") %(kind)
466
467 if not kind.validate_tree():
468 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
469 "construct_kinds, with kind = '%s'") %(kind)
470
471 def _construct_inner_namespaces(self, parent, depth=0):
472 #parent is InnerNamespace or Kind
473 ins_dict = self._dictionary_by_name(parent.namespaces)
474 for name, ins in ins_dict.iteritems():
475 ins._leafs = []
476
477 for p in parent._leafs:
478 ins_list = p.get_inner_namespace_list()
479
480 if len(ins_list) > depth:
481 ins_str = ins_list[depth]
482 ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent))
483 ins_dict[ins_str] = ins
484
485 if p not in ins._leafs:
486 ins._leafs.append(p)
487
488 for name, ins in ins_dict.iteritems():
489 ins.validate_tree()
490 # construct children INS
491 self._construct_inner_namespaces(ins, depth + 1)
492 ins.validate_tree()
493 # construct children entries
494 self._construct_entries(ins, depth + 1)
495 ins.sort_children()
496
497 if ins not in parent.namespaces:
498 parent._namespaces.append(ins)
499
500 if not ins.validate_tree():
501 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
502 "construct_inner_namespaces, with ins = '%s'") \
503 %(ins)
504
505 # doesnt construct the entries, so much as links them
506 def _construct_entries(self, parent, depth=0):
507 #parent is InnerNamespace or Kind
508 entry_dict = self._dictionary_by_name(parent.entries)
509 for p in parent._leafs:
510 ins_list = p.get_inner_namespace_list()
511
512 if len(ins_list) == depth:
513 entry = entry_dict.get(p.name, p)
514 entry_dict[p.name] = entry
515
516 for name, entry in entry_dict.iteritems():
517
518 old_parent = entry.parent
519 entry._parent = parent
520
521 if entry not in parent.entries:
522 parent._entries.append(entry)
523
524 if old_parent is not None and old_parent != parent:
525 print >> sys.stderr, ("ERROR: Parent changed from '%s' to '%s' for " + \
526 "entry '%s'") \
527 %(old_parent.name, parent.name, entry.name)
528
529 def _get_children(self):
530 if self.outer_namespaces is not None:
531 for i in self.outer_namespaces:
532 yield i
533
534 if self.tags is not None:
535 for i in self.tags:
536 yield i
537
538class Tag(Node):
539 """
540 A tag Node corresponding to a top-level <tag> element.
541
542 Attributes (Read-Only):
543 name: alias for id
544 id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC'
545 description: The description of the tag, the contents of the <tag> element.
546 parent: An edge to the parent, which is always the Metadata root node.
547 entries: A sequence of edges to entries/clones that are using this Tag.
548 """
549 def __init__(self, name, parent, description=""):
550 self._name = name # 'id' attribute in XML
551 self._id = name
552 self._description = description
553 self._parent = parent
554
555 # all entries that have this tag, including clones
556 self._entries = []
557
558 @property
559 def id(self):
560 return self._id
561
562 @property
563 def description(self):
564 return self._description
565
566 @property
567 def entries(self):
568 return (i for i in self._entries)
569
570 def _get_children(self):
571 return None
572
573class OuterNamespace(Node):
574 """
575 A node corresponding to a <namespace> element under <metadata>
576
577 Attributes (Read-Only):
578 name: The name attribute of the <namespace name="foo"> element.
579 parent: An edge to the parent, which is always the Metadata root node.
580 sections: A sequence of Section children.
581 """
582 def __init__(self, name, parent, sections=[]):
583 self._name = name
584 self._parent = parent # MetadataSet
585 self._sections = sections[:]
586 self._leafs = []
587
588 self._children = self._sections
589
590 @property
591 def sections(self):
592 return (i for i in self._sections)
593
594class Section(Node):
595 """
596 A node corresponding to a <section> element under <namespace>
597
598 Attributes (Read-Only):
599 name: The name attribute of the <section name="foo"> element.
600 parent: An edge to the parent, which is always an OuterNamespace instance.
601 description: A string description of the section, or None.
602 kinds: A sequence of Kind children.
603 """
604 def __init__(self, name, parent, description=None, kinds=[]):
605 self._name = name
606 self._parent = parent
607 self._description = description
608 self._kinds = kinds[:]
609
610 self._leafs = []
611
612
613 @property
614 def description(self):
615 return self._description
616
617 @property
618 def kinds(self):
619 return (i for i in self._kinds)
620
621 def sort_children(self):
622 self.validate_tree()
623 # order is always controls,static,dynamic
624 find_child = lambda x: [i for i in self._get_children() if i.name == x]
625 new_lst = find_child('controls') \
626 + find_child('static') \
627 + find_child('dynamic')
628 self._kinds = new_lst
629 self.validate_tree()
630
631 def _get_children(self):
632 return (i for i in self.kinds)
633
634class Kind(Node):
635 """
636 A node corresponding to one of: <static>,<dynamic>,<controls> under a
637 <section> element.
638
639 Attributes (Read-Only):
640 name: A string which is one of 'static', 'dynamic, or 'controls'.
641 parent: An edge to the parent, which is always a Section instance.
642 namespaces: A sequence of InnerNamespace children.
643 entries: A sequence of Entry/Clone children.
644 """
645 def __init__(self, name, parent):
646 self._name = name
647 self._parent = parent
648 self._namespaces = []
649 self._entries = []
650
651 self._leafs = []
652
653 @property
654 def namespaces(self):
655 return self._namespaces
656
657 @property
658 def entries(self):
659 return self._entries
660
661 def sort_children(self):
662 self._namespaces.sort(key=self._get_name())
663 self._entries.sort(key=self._get_name())
664
665 def _get_children(self):
666 for i in self.namespaces:
667 yield i
668 for i in self.entries:
669 yield i
670
671class InnerNamespace(Node):
672 """
673 A node corresponding to a <namespace> which is an ancestor of a Kind.
674 These namespaces may have other namespaces recursively, or entries as leafs.
675
676 Attributes (Read-Only):
677 name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
678 parent: An edge to the parent, which is an InnerNamespace or a Kind.
679 namespaces: A sequence of InnerNamespace children.
680 entries: A sequence of Entry/Clone children.
681 """
682 def __init__(self, name, parent):
683 self._name = name
684 self._parent = parent
685 self._namespaces = []
686 self._entries = []
687 self._leafs = []
688
689 @property
690 def namespaces(self):
691 return self._namespaces
692
693 @property
694 def entries(self):
695 return self._entries
696
697 def sort_children(self):
698 self._namespaces.sort(key=self._get_name())
699 self._entries.sort(key=self._get_name())
700
701 def _get_children(self):
702 for i in self.namespaces:
703 yield i
704 for i in self.entries:
705 yield i
706
707class EnumValue(object):
708 """
709 A class corresponding to a <value> element within an <enum> within an <entry>.
710
711 Attributes (Read-Only):
712 name: A string, e.g. 'ON' or 'OFF'
713 id: An optional numeric string, e.g. '0' or '0xFF'
714 optional: A boolean
715 notes: A string describing the notes, or None.
716 """
717 def __init__(self, name, id=None, optional=False, notes=None):
718 self._name = name # str, e.g. 'ON' or 'OFF'
719 self._id = id # int, e.g. '0'
720 self._optional = optional # bool
721 self._notes = notes # None or str
722
723 @property
724 def name(self):
725 return self._name
726
727 @property
728 def id(self):
729 return self._id
730
731 @property
732 def optional(self):
733 return self._optional
734
735 @property
736 def notes(self):
737 return self._notes
738
739class Enum(object):
740 """
741 A class corresponding to an <enum> element within an <entry>.
742
743 Attributes (Read-Only):
744 parent: An edge to the parent, always an Entry instance.
745 values: A sequence of EnumValue children.
746 """
747 def __init__(self, parent, values, ids={}, optionals=[], notes={}):
748 self._values = \
749 [ EnumValue(val, ids.get(val), val in optionals, notes.get(val)) \
750 for val in values ]
751
752 self._parent = parent
753
754 @property
755 def parent(self):
756 return self._parent
757
758 @property
759 def values(self):
760 return (i for i in self._values)
761
762class Entry(Node):
763 """
764 A node corresponding to an <entry> element.
765
766 Attributes (Read-Only):
767 parent: An edge to the parent node, which is an InnerNamespace or Kind.
768 name: The fully qualified name string, e.g. 'android.shading.mode'
769 name_short: The name attribute from <entry name="mode">, e.g. mode
770 type: The type attribute from <entry type="bar">
771 kind: A string ('static', 'dynamic', 'controls') corresponding to the
772 ancestor Kind#name
773 container: The container attribute from <entry container="array">, or None.
774 container_sizes: A sequence of size strings or None if container is None.
775 enum: An Enum instance if type is 'enum', None otherwise.
776 tuple_values: A sequence of strings describing the tuple values,
777 None if container is not 'tuple'.
778 description: A string description, or None.
779 range: A string range, or None.
780 units: A string units, or None.
781 tags: A sequence of Tag nodes associated with this Entry.
782 type_notes: A string describing notes for the type, or None.
783
784 Remarks:
785 Subclass Clone can be used interchangeable with an Entry,
786 for when we don't care about the underlying type.
787
788 parent and tags edges are invalid until after Metadata#construct_graph
789 has been invoked.
790 """
791 def __init__(self, **kwargs):
792 """
793 Instantiate a new Entry node.
794
795 Args:
796 name: A string with the fully qualified name, e.g. 'android.shading.mode'
797 type: A string describing the type, e.g. 'int32'
798 kind: A string describing the kind, e.g. 'static'
799
800 Args (if container):
801 container: A string describing the container, e.g. 'array' or 'tuple'
802 container_sizes: A list of string sizes if a container, or None otherwise
803
804 Args (if container is 'tuple'):
805 tuple_values: A list of tuple values, e.g. ['width', 'height']
806
807 Args (if type is 'enum'):
808 enum_values: A list of value strings, e.g. ['ON', 'OFF']
809 enum_optionals: A list of optional enum values, e.g. ['OFF']
810 enum_notes: A dictionary of value->notes strings.
811 enum_ids: A dictionary of value->id strings.
812
813 Args (optional):
814 description: A string with a description of the entry.
815 range: A string with the range of the values of the entry, e.g. '>= 0'
816 units: A string with the units of the values, e.g. 'inches'
817 notes: A string with the notes for the entry
818 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
819 type_notes: A string with the notes for the type
820 """
821
822 if kwargs.get('type') is None:
823 print >> sys.stderr, "ERROR: Missing type for entry '%s' kind '%s'" \
824 %(kwargs.get('name'), kwargs.get('kind'))
825
826 # Attributes are Read-Only, but edges may be mutated by
827 # Metadata, particularly during construct_graph
828
829 self._name = kwargs['name']
830 self._type = kwargs['type']
831 self._kind = kwargs['kind'] # static, dynamic, or controls
832
833 self._init_common(**kwargs)
834
835 @property
836 def type(self):
837 return self._type
838
839 @property
840 def kind(self):
841 return self._kind
842
843 @property
844 def name_short(self):
845 return self.get_name_minimal()
846
847 @property
848 def container(self):
849 return self._container
850
851 @property
852 def container_sizes(self):
853 if self._container_sizes is None:
854 return None
855 else:
856 return (i for i in self._container_sizes)
857
858 @property
859 def tuple_values(self):
860 if self._tuple_values is None:
861 return None
862 else:
863 return (i for i in self._tuple_values)
864
865 @property
866 def description(self):
867 return self._description
868
869 @property
870 def range(self):
871 return self._range
872
873 @property
874 def units(self):
875 return self._units
876
877 @property
878 def notes(self):
879 return self._notes
880
881 @property
882 def tags(self):
883 if self._tags is None:
884 return None
885 else:
886 return (i for i in self._tags)
887
888 @property
889 def type_notes(self):
890 return self._type_notes
891
892 @property
893 def enum(self):
894 return self._enum
895
896 def _get_children(self):
897 return None
898
899 def sort_children(self):
900 return None
901
902 def is_clone(self):
903 """
904 Whether or not this is a Clone instance.
905
906 Returns:
907 False
908 """
909 return False
910
911 def _init_common(self, **kwargs):
912
913 self._parent = None # filled in by MetadataSet::_construct_entries
914
915 self._container = kwargs.get('container')
916 self._container_sizes = kwargs.get('container_sizes')
917
918 # access these via the 'enum' prop
919 enum_values = kwargs.get('enum_values')
920 enum_optionals = kwargs.get('enum_optionals')
921 enum_notes = kwargs.get('enum_notes') # { value => notes }
922 enum_ids = kwargs.get('enum_ids') # { value => notes }
923 self._tuple_values = kwargs.get('tuple_values')
924
925 self._description = kwargs.get('description')
926 self._range = kwargs.get('range')
927 self._units = kwargs.get('units')
928 self._notes = kwargs.get('notes')
929
930 self._tag_ids = kwargs.get('tag_ids', [])
931 self._tags = None # Filled in by MetadataSet::_construct_tags
932
933 self._type_notes = kwargs.get('type_notes')
934
935 if self._type == 'enum':
936 self._enum = Enum(self, enum_values, enum_ids, enum_optionals, enum_notes)
937
938 self._property_keys = kwargs
939
940 # Helpers for accessing less than the fully qualified name
941
942 def get_name_as_list(self):
943 """
944 Returns the name as a list split by a period.
945
946 For example:
947 entry.name is 'android.lens.info.shading'
948 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
949 """
950 return self.name.split(".")
951
952 def get_inner_namespace_list(self):
953 """
954 Returns the inner namespace part of the name as a list
955
956 For example:
957 entry.name is 'android.lens.info.shading'
958 entry.get_inner_namespace_list() == ['info']
959 """
960 return self.get_name_as_list()[2:-1]
961
962 def get_outer_namespace(self):
963 """
964 Returns the outer namespace as a string.
965
966 For example:
967 entry.name is 'android.lens.info.shading'
968 entry.get_outer_namespace() == 'android'
969
970 Remarks:
971 Since outer namespaces are non-recursive,
972 and each entry has one, this does not need to be a list.
973 """
974 return self.get_name_as_list()[0]
975
976 def get_section(self):
977 """
978 Returns the section as a string.
979
980 For example:
981 entry.name is 'android.lens.info.shading'
982 entry.get_section() == ''
983
984 Remarks:
985 Since outer namespaces are non-recursive,
986 and each entry has one, this does not need to be a list.
987 """
988 return self.get_name_as_list()[1]
989
990 def get_name_minimal(self):
991 """
992 Returns only the last component of the fully qualified name as a string.
993
994 For example:
995 entry.name is 'android.lens.info.shading'
996 entry.get_name_minimal() == 'shading'
997
998 Remarks:
999 entry.name_short it an alias for this
1000 """
1001 return self.get_name_as_list()[-1]
1002
1003class Clone(Entry):
1004 """
1005 A Node corresponding to a <clone> element. It has all the attributes of an
1006 <entry> element (Entry) plus the additions specified below.
1007
1008 Attributes (Read-Only):
1009 entry: an edge to an Entry object that this targets
1010 target_kind: A string describing the kind of the target entry.
1011 name: a string of the name, same as entry.name
1012 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1013 for the <clone> element.
1014 type: always None, since a clone cannot override the type.
1015 """
1016 def __init__(self, entry=None, **kwargs):
1017 """
1018 Instantiate a new Clone node.
1019
1020 Args:
1021 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1022 type: A string describing the type, e.g. 'int32'
1023 kind: A string describing the kind, e.g. 'static'
1024 target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1025
1026 Args (if container):
1027 container: A string describing the container, e.g. 'array' or 'tuple'
1028 container_sizes: A list of string sizes if a container, or None otherwise
1029
1030 Args (if container is 'tuple'):
1031 tuple_values: A list of tuple values, e.g. ['width', 'height']
1032
1033 Args (if type is 'enum'):
1034 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1035 enum_optionals: A list of optional enum values, e.g. ['OFF']
1036 enum_notes: A dictionary of value->notes strings.
1037 enum_ids: A dictionary of value->id strings.
1038
1039 Args (optional):
1040 entry: An edge to the corresponding target Entry.
1041 description: A string with a description of the entry.
1042 range: A string with the range of the values of the entry, e.g. '>= 0'
1043 units: A string with the units of the values, e.g. 'inches'
1044 notes: A string with the notes for the entry
1045 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1046 type_notes: A string with the notes for the type
1047
1048 Remarks:
1049 Note that type is not specified since it has to be the same as the
1050 entry.type.
1051 """
1052 self._entry = entry # Entry object
1053 self._target_kind = kwargs['target_kind']
1054 self._name = kwargs['name'] # same as entry.name
1055 self._kind = kwargs['kind']
1056
1057 # illegal to override the type, it should be the same as the entry
1058 self._type = None
1059 # the rest of the kwargs are optional
1060 # can be used to override the regular entry data
1061 self._init_common(**kwargs)
1062
1063 @property
1064 def entry(self):
1065 return self._entry
1066
1067 @property
1068 def target_kind(self):
1069 return self._target_kind
1070
1071 def is_clone(self):
1072 """
1073 Whether or not this is a Clone instance.
1074
1075 Returns:
1076 True
1077 """
1078 return True
1079
1080
1081
1082