blob: e059e333902c912c26c1902e6e21adadbf1e4460 [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.
Igor Murashkinaa133d32013-06-28 17:27:49 -070026 MergedEntry: A node corresponding to either <entry> or <clone> elements.
Igor Murashkin77b63ca2012-11-09 16:15:02 -080027 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>
Igor Murashkinaa133d32013-06-28 17:27:49 -070032 EnumValue: A class corresponding to a <value> element within an Enum
Igor Murashkin77b63ca2012-11-09 16:15:02 -080033 Metadata: Root node that also provides tree construction functionality.
34 Tag: A node corresponding to a top level <tag> element.
Igor Murashkinb8dc8812013-07-17 16:29:34 -070035 Typedef: A node corresponding to a <typedef> element under <types>.
Igor Murashkin77b63ca2012-11-09 16:15:02 -080036"""
37
38import sys
Igor Murashkin5804a482012-12-05 13:06:59 -080039import itertools
Igor Murashkin586c8612012-11-29 17:08:36 -080040from collections import OrderedDict
Igor Murashkin77b63ca2012-11-09 16:15:02 -080041
42class Node(object):
43 """
44 Base class for most nodes that are part of the Metadata graph.
45
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.
50 """
51
52 def __init__(self):
53 self._parent = None
54 self._name = None
55
56 @property
57 def parent(self):
58 return self._parent
59
60 @property
61 def name(self):
62 return self._name
63
64 def find_all(self, pred):
65 """
66 Find all descendants that match the predicate.
67
68 Args:
69 pred: a predicate function that acts as a filter for a Node
70
71 Yields:
72 A sequence of all descendants for which pred(node) is true,
73 in a pre-order visit order.
74 """
75 if pred(self):
76 yield self
77
78 if self._get_children() is None:
79 return
80
81 for i in self._get_children():
82 for j in i.find_all(pred):
83 yield j
84
Igor Murashkin77b63ca2012-11-09 16:15:02 -080085 def find_first(self, pred):
86 """
87 Find the first descendant that matches the predicate.
88
89 Args:
90 pred: a predicate function that acts as a filter for a Node
91
92 Returns:
93 The first Node from find_all(pred), or None if there were no results.
94 """
95 for i in self.find_all(pred):
96 return i
97
98 return None
99
100 def find_parent_first(self, pred):
101 """
102 Find the first ancestor that matches the predicate.
103
104 Args:
105 pred: A predicate function that acts as a filter for a Node
106
107 Returns:
108 The first ancestor closest to the node for which pred(node) is true.
109 """
110 for i in self.find_parents(pred):
111 return i
112
113 return None
114
115 def find_parents(self, pred):
116 """
117 Find all ancestors that match the predicate.
118
119 Args:
120 pred: A predicate function that acts as a filter for a Node
121
122 Yields:
123 A sequence of all ancestors (closest to furthest) from the node,
124 where pred(node) is true.
125 """
126 parent = self.parent
127
128 while parent is not None:
129 if pred(parent):
130 yield parent
131 parent = parent.parent
132
133 def sort_children(self):
134 """
135 Sorts the immediate children in-place.
136 """
137 self._sort_by_name(self._children)
138
139 def _sort_by_name(self, what):
140 what.sort(key=lambda x: x.name)
141
142 def _get_name(self):
143 return lambda x: x.name
144
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)
148
149 def _children_name_map_matching(self, match=lambda x: True):
150 d = {}
Igor Murashkinaa133d32013-06-28 17:27:49 -0700151 for i in self._get_children():
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800152 if match(i):
153 d[i.name] = i
154 return d
155
156 @staticmethod
157 def _dictionary_by_name(values):
Igor Murashkin586c8612012-11-29 17:08:36 -0800158 d = OrderedDict()
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800159 for i in values:
160 d[i.name] = i
161
162 return d
163
164 def validate_tree(self):
165 """
166 Sanity check the tree recursively, ensuring for a node n, all children's
167 parents are also n.
168
169 Returns:
170 True if validation succeeds, False otherwise.
171 """
172 succ = True
173 children = self._get_children()
174 if children is None:
175 return True
176
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)
182 succ = False
183
184 succ = child.validate_tree() and succ
185
186 return succ
187
188 def __str__(self):
189 return "<%s name='%s'>" %(self.__class__, self.name)
190
191class Metadata(Node):
192 """
193 A node corresponding to a <metadata> entry.
194
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.
Igor Murashkinb8dc8812013-07-17 16:29:34 -0700199 types: An iterable of all Typedef instances available in the graph.
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800200 """
201
202 def __init__(self):
203 """
204 Initialize with no children. Use insert_* functions and then
205 construct_graph() to build up the Metadata from some source.
206 """
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800207# Private
208 self._entries = []
209 # kind => { name => entry }
210 self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} }
Igor Murashkin586c8612012-11-29 17:08:36 -0800211 self._entries_ordered = [] # list of ordered Entry/Clone instances
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800212 self._clones = []
213
214# Public (Read Only)
Eino-Ville Talvala63c0fb22014-01-02 16:11:44 -0800215 self._name = None
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800216 self._parent = None
217 self._outer_namespaces = None
218 self._tags = []
Igor Murashkinb8dc8812013-07-17 16:29:34 -0700219 self._types = []
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800220
221 @property
222 def outer_namespaces(self):
223 if self._outer_namespaces is None:
224 return None
225 else:
226 return (i for i in self._outer_namespaces)
227
228 @property
229 def tags(self):
230 return (i for i in self._tags)
231
Igor Murashkinb8dc8812013-07-17 16:29:34 -0700232 @property
233 def types(self):
234 return (i for i in self._types)
235
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800236 def _get_properties(self):
237
238 for i in self._entries:
239 yield i
240
241 for i in self._clones:
242 yield i
243
244 def insert_tag(self, tag, description=""):
245 """
246 Insert a tag into the metadata.
247
248 Args:
249 tag: A string identifier for a tag.
250 description: A string description for a tag.
251
252 Example:
253 metadata.insert_tag("BC", "Backwards Compatibility for old API")
254
255 Remarks:
256 Subsequent calls to insert_tag with the same tag are safe (they will
257 be ignored).
258 """
259 tag_ids = [tg.name for tg in self.tags if tg.name == tag]
260 if not tag_ids:
Igor Murashkin6ad61d42012-11-21 09:44:05 -0800261 self._tags.append(Tag(tag, self, description))
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800262
Igor Murashkinb8dc8812013-07-17 16:29:34 -0700263 def insert_type(self, type_name, type_selector="typedef", **kwargs):
264 """
265 Insert a type into the metadata.
266
267 Args:
268 type_name: A type's name
269 type_selector: The selector for the type, e.g. 'typedef'
270
271 Args (if type_selector == 'typedef'):
272 languages: A map of 'language name' -> 'fully qualified class path'
273
274 Example:
275 metadata.insert_type('rectangle', 'typedef',
276 { 'java': 'android.graphics.Rect' })
277
278 Remarks:
279 Subsequent calls to insert_type with the same type name are safe (they
280 will be ignored)
281 """
282
283 if type_selector != 'typedef':
284 raise ValueError("Unsupported type_selector given " + type_selector)
285
286 type_names = [tp.name for tp in self.types if tp.name == tp]
287 if not type_names:
288 self._types.append(Typedef(type_name, self, kwargs.get('languages')))
289
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800290 def insert_entry(self, entry):
291 """
292 Insert an entry into the metadata.
293
294 Args:
295 entry: A key-value dictionary describing an entry. Refer to
296 Entry#__init__ for the keys required/optional.
297
298 Remarks:
299 Subsequent calls to insert_entry with the same entry+kind name are safe
300 (they will be ignored).
301 """
302 e = Entry(**entry)
303 self._entries.append(e)
304 self._entry_map[e.kind][e.name] = e
Igor Murashkin586c8612012-11-29 17:08:36 -0800305 self._entries_ordered.append(e)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800306
307 def insert_clone(self, clone):
308 """
309 Insert a clone into the metadata.
310
311 Args:
312 clone: A key-value dictionary describing a clone. Refer to
313 Clone#__init__ for the keys required/optional.
314
315 Remarks:
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.
319 """
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800320 # figure out corresponding entry later. allow clone insert, entry insert
321 entry = None
322 c = Clone(entry, **clone)
323 self._entry_map[c.kind][c.name] = c
324 self._clones.append(c)
Igor Murashkin586c8612012-11-29 17:08:36 -0800325 self._entries_ordered.append(c)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800326
327 def prune_clones(self):
328 """
329 Remove all clones that don't point to an existing entry.
330
331 Remarks:
332 This should be called after all insert_entry/insert_clone calls have
333 finished.
334 """
335 remove_list = []
336 for p in self._clones:
337 if p.entry is None:
338 remove_list.append(p)
339
340 for p in remove_list:
341
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
Igor Murashkinaa133d32013-06-28 17:27:49 -0700346 for ancestor in p.find_parents(lambda x: not isinstance(x, Metadata)):
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800347 ancestor._leafs.remove(p)
348
349 # remove from global list
350 self._clones.remove(p)
351 self._entry_map[p.kind].pop(p.name)
Igor Murashkin586c8612012-11-29 17:08:36 -0800352 self._entries_ordered.remove(p)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800353
Yin-Chia Yeh94f013b2016-04-06 15:59:39 -0700354 def is_entry_this_kind(self, entry, kind):
355 """
356 Check if input entry if of input kind
357
358 Args:
359 entry: an Entry object
360 kind: a string. Possible values are "static", "dynamic", "controls"
361
362 Returns:
363 A boolean indicating whether input entry is of input kind.
364 """
365 if kind not in ("static", "dynamic", "controls"):
366 assert(False), "Unknown kind value " + kind
367
368 return entry.name in self._entry_map[kind]
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800369
370 # After all entries/clones are inserted,
371 # invoke this to generate the parent/child node graph all these objects
372 def construct_graph(self):
373 """
374 Generate the graph recursively, after which all Entry nodes will be
375 accessible recursively by crawling through the outer_namespaces sequence.
376
377 Remarks:
378 This is safe to be called multiple times at any time. It should be done at
379 least once or there will be no graph.
380 """
381 self.validate_tree()
382 self._construct_tags()
383 self.validate_tree()
Igor Murashkinb8dc8812013-07-17 16:29:34 -0700384 self._construct_types()
385 self.validate_tree()
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800386 self._construct_clones()
387 self.validate_tree()
388 self._construct_outer_namespaces()
389 self.validate_tree()
390
391 def _construct_tags(self):
392 tag_dict = self._dictionary_by_name(self.tags)
393 for p in self._get_properties():
394 p._tags = []
395 for tag_id in p._tag_ids:
396 tag = tag_dict.get(tag_id)
397
398 if tag not in p._tags:
399 p._tags.append(tag)
400
Igor Murashkin617da162012-11-29 13:35:15 -0800401 if p not in tag.entries:
402 tag._entries.append(p)
403
Igor Murashkinb8dc8812013-07-17 16:29:34 -0700404 def _construct_types(self):
405 type_dict = self._dictionary_by_name(self.types)
406 for p in self._get_properties():
407 if p._type_name:
408 type_node = type_dict.get(p._type_name)
409 p._typedef = type_node
410
411 if p not in type_node.entries:
412 type_node._entries.append(p)
413
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800414 def _construct_clones(self):
415 for p in self._clones:
416 target_kind = p.target_kind
417 target_entry = self._entry_map[target_kind].get(p.name)
418 p._entry = target_entry
419
420 # should not throw if we pass validation
421 # but can happen when importing obsolete CSV entries
422 if target_entry is None:
423 print >> sys.stderr, ("WARNING: Clone entry '%s' target kind '%s'" + \
424 " has no corresponding entry") \
425 %(p.name, p.target_kind)
426
427 def _construct_outer_namespaces(self):
428
429 if self._outer_namespaces is None: #the first time this runs
430 self._outer_namespaces = []
431
432 root = self._dictionary_by_name(self._outer_namespaces)
433 for ons_name, ons in root.iteritems():
434 ons._leafs = []
435
Igor Murashkin586c8612012-11-29 17:08:36 -0800436 for p in self._entries_ordered:
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800437 ons_name = p.get_outer_namespace()
438 ons = root.get(ons_name, OuterNamespace(ons_name, self))
439 root[ons_name] = ons
440
441 if p not in ons._leafs:
442 ons._leafs.append(p)
443
444 for ons_name, ons in root.iteritems():
445
446 ons.validate_tree()
447
448 self._construct_sections(ons)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800449
450 if ons not in self._outer_namespaces:
451 self._outer_namespaces.append(ons)
452
453 ons.validate_tree()
454
455 def _construct_sections(self, outer_namespace):
456
457 sections_dict = self._dictionary_by_name(outer_namespace.sections)
458 for sec_name, sec in sections_dict.iteritems():
459 sec._leafs = []
460 sec.validate_tree()
461
462 for p in outer_namespace._leafs:
463 does_exist = sections_dict.get(p.get_section())
464
465 sec = sections_dict.get(p.get_section(), \
466 Section(p.get_section(), outer_namespace))
467 sections_dict[p.get_section()] = sec
468
469 sec.validate_tree()
470
471 if p not in sec._leafs:
472 sec._leafs.append(p)
473
474 for sec_name, sec in sections_dict.iteritems():
475
476 if not sec.validate_tree():
477 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
478 "construct_sections (start), with section = '%s'")\
479 %(sec)
480
481 self._construct_kinds(sec)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800482
483 if sec not in outer_namespace.sections:
484 outer_namespace._sections.append(sec)
485
486 if not sec.validate_tree():
487 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
488 "construct_sections (end), with section = '%s'") \
489 %(sec)
490
491 # 'controls', 'static' 'dynamic'. etc
492 def _construct_kinds(self, section):
Igor Murashkin5804a482012-12-05 13:06:59 -0800493 for kind in section.kinds:
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800494 kind._leafs = []
495 section.validate_tree()
496
Igor Murashkin5804a482012-12-05 13:06:59 -0800497 group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind)
498 leaf_it = ((k, g) for k, g in group_entry_by_kind)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800499
Igor Murashkin5804a482012-12-05 13:06:59 -0800500 # allow multiple kinds with the same name. merge if adjacent
501 # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic
502 # this helps maintain ABI compatibility when adding an entry in a new kind
503 for idx, (kind_name, entry_it) in enumerate(leaf_it):
504 if idx >= len(section._kinds):
505 kind = Kind(kind_name, section)
506 section._kinds.append(kind)
507 section.validate_tree()
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800508
Igor Murashkin5804a482012-12-05 13:06:59 -0800509 kind = section._kinds[idx]
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800510
Igor Murashkin5804a482012-12-05 13:06:59 -0800511 for p in entry_it:
512 if p not in kind._leafs:
513 kind._leafs.append(p)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800514
Igor Murashkin5804a482012-12-05 13:06:59 -0800515 for kind in section._kinds:
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800516 kind.validate_tree()
517 self._construct_inner_namespaces(kind)
518 kind.validate_tree()
519 self._construct_entries(kind)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800520 kind.validate_tree()
521
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800522 if not section.validate_tree():
523 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
524 "construct_kinds, with kind = '%s'") %(kind)
525
526 if not kind.validate_tree():
527 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
528 "construct_kinds, with kind = '%s'") %(kind)
529
530 def _construct_inner_namespaces(self, parent, depth=0):
531 #parent is InnerNamespace or Kind
532 ins_dict = self._dictionary_by_name(parent.namespaces)
533 for name, ins in ins_dict.iteritems():
534 ins._leafs = []
535
536 for p in parent._leafs:
537 ins_list = p.get_inner_namespace_list()
538
539 if len(ins_list) > depth:
540 ins_str = ins_list[depth]
541 ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent))
542 ins_dict[ins_str] = ins
543
544 if p not in ins._leafs:
545 ins._leafs.append(p)
546
547 for name, ins in ins_dict.iteritems():
548 ins.validate_tree()
549 # construct children INS
550 self._construct_inner_namespaces(ins, depth + 1)
551 ins.validate_tree()
552 # construct children entries
553 self._construct_entries(ins, depth + 1)
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800554
555 if ins not in parent.namespaces:
556 parent._namespaces.append(ins)
557
558 if not ins.validate_tree():
559 print >> sys.stderr, ("ERROR: Failed to validate tree in " + \
560 "construct_inner_namespaces, with ins = '%s'") \
561 %(ins)
562
563 # doesnt construct the entries, so much as links them
564 def _construct_entries(self, parent, depth=0):
565 #parent is InnerNamespace or Kind
566 entry_dict = self._dictionary_by_name(parent.entries)
567 for p in parent._leafs:
568 ins_list = p.get_inner_namespace_list()
569
570 if len(ins_list) == depth:
571 entry = entry_dict.get(p.name, p)
572 entry_dict[p.name] = entry
573
574 for name, entry in entry_dict.iteritems():
575
576 old_parent = entry.parent
577 entry._parent = parent
578
579 if entry not in parent.entries:
580 parent._entries.append(entry)
581
582 if old_parent is not None and old_parent != parent:
583 print >> sys.stderr, ("ERROR: Parent changed from '%s' to '%s' for " + \
584 "entry '%s'") \
585 %(old_parent.name, parent.name, entry.name)
586
587 def _get_children(self):
588 if self.outer_namespaces is not None:
589 for i in self.outer_namespaces:
590 yield i
591
592 if self.tags is not None:
593 for i in self.tags:
594 yield i
595
596class Tag(Node):
597 """
598 A tag Node corresponding to a top-level <tag> element.
599
600 Attributes (Read-Only):
601 name: alias for id
602 id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC'
603 description: The description of the tag, the contents of the <tag> element.
604 parent: An edge to the parent, which is always the Metadata root node.
605 entries: A sequence of edges to entries/clones that are using this Tag.
606 """
607 def __init__(self, name, parent, description=""):
608 self._name = name # 'id' attribute in XML
609 self._id = name
610 self._description = description
611 self._parent = parent
612
613 # all entries that have this tag, including clones
Igor Murashkin617da162012-11-29 13:35:15 -0800614 self._entries = [] # filled in by Metadata#construct_tags
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800615
616 @property
617 def id(self):
618 return self._id
619
620 @property
621 def description(self):
622 return self._description
623
624 @property
625 def entries(self):
626 return (i for i in self._entries)
627
628 def _get_children(self):
629 return None
630
Igor Murashkinb8dc8812013-07-17 16:29:34 -0700631class Typedef(Node):
632 """
633 A typedef Node corresponding to a <typedef> element under a top-level <types>.
634
635 Attributes (Read-Only):
636 name: The name of this typedef as a string.
637 languages: A dictionary of 'language name' -> 'fully qualified class'.
638 parent: An edge to the parent, which is always the Metadata root node.
639 entries: An iterable over all entries which reference this typedef.
640 """
641 def __init__(self, name, parent, languages=None):
642 self._name = name
643 self._parent = parent
644
645 # all entries that have this typedef
646 self._entries = [] # filled in by Metadata#construct_types
647
648 self._languages = languages or {}
649
650 @property
651 def languages(self):
652 return self._languages
653
654 @property
655 def entries(self):
656 return (i for i in self._entries)
657
658 def _get_children(self):
659 return None
660
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800661class OuterNamespace(Node):
662 """
663 A node corresponding to a <namespace> element under <metadata>
664
665 Attributes (Read-Only):
666 name: The name attribute of the <namespace name="foo"> element.
667 parent: An edge to the parent, which is always the Metadata root node.
668 sections: A sequence of Section children.
669 """
670 def __init__(self, name, parent, sections=[]):
671 self._name = name
672 self._parent = parent # MetadataSet
673 self._sections = sections[:]
674 self._leafs = []
675
676 self._children = self._sections
677
678 @property
679 def sections(self):
680 return (i for i in self._sections)
681
682class Section(Node):
683 """
684 A node corresponding to a <section> element under <namespace>
685
686 Attributes (Read-Only):
687 name: The name attribute of the <section name="foo"> element.
688 parent: An edge to the parent, which is always an OuterNamespace instance.
689 description: A string description of the section, or None.
690 kinds: A sequence of Kind children.
Igor Murashkin5804a482012-12-05 13:06:59 -0800691 merged_kinds: A sequence of virtual Kind children,
692 with each Kind's children merged by the kind.name
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800693 """
694 def __init__(self, name, parent, description=None, kinds=[]):
695 self._name = name
696 self._parent = parent
697 self._description = description
698 self._kinds = kinds[:]
699
700 self._leafs = []
701
702
703 @property
704 def description(self):
705 return self._description
706
707 @property
708 def kinds(self):
709 return (i for i in self._kinds)
710
711 def sort_children(self):
712 self.validate_tree()
713 # order is always controls,static,dynamic
714 find_child = lambda x: [i for i in self._get_children() if i.name == x]
715 new_lst = find_child('controls') \
716 + find_child('static') \
717 + find_child('dynamic')
718 self._kinds = new_lst
719 self.validate_tree()
720
721 def _get_children(self):
722 return (i for i in self.kinds)
723
Igor Murashkin5804a482012-12-05 13:06:59 -0800724 @property
725 def merged_kinds(self):
726
727 def aggregate_by_name(acc, el):
728 existing = [i for i in acc if i.name == el.name]
729 if existing:
730 k = existing[0]
731 else:
732 k = Kind(el.name, el.parent)
733 acc.append(k)
734
735 k._namespaces.extend(el._namespaces)
736 k._entries.extend(el._entries)
737
738 return acc
739
740 new_kinds_lst = reduce(aggregate_by_name, self.kinds, [])
741
742 for k in new_kinds_lst:
743 yield k
744
Igor Murashkinaa133d32013-06-28 17:27:49 -0700745 def combine_kinds_into_single_node(self):
746 r"""
747 Combines the section's Kinds into a single node.
748
749 Combines all the children (kinds) of this section into a single
750 virtual Kind node.
751
752 Returns:
753 A new Kind node that collapses all Kind siblings into one, combining
754 all their children together.
755
756 For example, given self.kinds == [ x, y ]
757
758 x y z
759 / | | \ --> / | | \
760 a b c d a b c d
761
762 a new instance z is returned in this example.
763
764 Remarks:
765 The children of the kinds are the same references as before, that is
766 their parents will point to the old parents and not to the new parent.
767 """
768 combined = Kind(name="combined", parent=self)
769
770 for k in self._get_children():
771 combined._namespaces.extend(k.namespaces)
772 combined._entries.extend(k.entries)
773
774 return combined
775
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800776class Kind(Node):
777 """
778 A node corresponding to one of: <static>,<dynamic>,<controls> under a
779 <section> element.
780
781 Attributes (Read-Only):
782 name: A string which is one of 'static', 'dynamic, or 'controls'.
783 parent: An edge to the parent, which is always a Section instance.
784 namespaces: A sequence of InnerNamespace children.
785 entries: A sequence of Entry/Clone children.
Igor Murashkin617da162012-11-29 13:35:15 -0800786 merged_entries: A sequence of MergedEntry virtual nodes from entries
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800787 """
788 def __init__(self, name, parent):
789 self._name = name
790 self._parent = parent
791 self._namespaces = []
792 self._entries = []
793
794 self._leafs = []
795
796 @property
797 def namespaces(self):
798 return self._namespaces
799
800 @property
801 def entries(self):
802 return self._entries
803
Igor Murashkin617da162012-11-29 13:35:15 -0800804 @property
805 def merged_entries(self):
806 for i in self.entries:
807 yield i.merge()
808
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800809 def sort_children(self):
810 self._namespaces.sort(key=self._get_name())
811 self._entries.sort(key=self._get_name())
812
813 def _get_children(self):
814 for i in self.namespaces:
815 yield i
816 for i in self.entries:
817 yield i
818
Igor Murashkinaa133d32013-06-28 17:27:49 -0700819 def combine_children_by_name(self):
820 r"""
821 Combine multiple children with the same name into a single node.
822
823 Returns:
824 A new Kind where all of the children with the same name were combined.
825
826 For example:
827
828 Given a Kind k:
829
830 k
831 / | \
832 a b c
833 | | |
834 d e f
835
836 a.name == "foo"
837 b.name == "foo"
838 c.name == "bar"
839
840 The returned Kind will look like this:
841
842 k'
843 / \
844 a' c'
845 / | |
846 d e f
847
848 Remarks:
849 This operation is not recursive. To combine the grandchildren and other
850 ancestors, call this method on the ancestor nodes.
851 """
852 return Kind._combine_children_by_name(self, new_type=type(self))
853
854 # new_type is either Kind or InnerNamespace
855 @staticmethod
856 def _combine_children_by_name(self, new_type):
857 new_ins_dict = OrderedDict()
858 new_ent_dict = OrderedDict()
859
860 for ins in self.namespaces:
861 new_ins = new_ins_dict.setdefault(ins.name,
862 InnerNamespace(ins.name, parent=self))
863 new_ins._namespaces.extend(ins.namespaces)
864 new_ins._entries.extend(ins.entries)
865
866 for ent in self.entries:
867 new_ent = new_ent_dict.setdefault(ent.name,
868 ent.merge())
869
870 kind = new_type(self.name, self.parent)
871 kind._namespaces = new_ins_dict.values()
872 kind._entries = new_ent_dict.values()
873
874 return kind
875
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800876class InnerNamespace(Node):
877 """
878 A node corresponding to a <namespace> which is an ancestor of a Kind.
879 These namespaces may have other namespaces recursively, or entries as leafs.
880
881 Attributes (Read-Only):
882 name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo'
883 parent: An edge to the parent, which is an InnerNamespace or a Kind.
884 namespaces: A sequence of InnerNamespace children.
885 entries: A sequence of Entry/Clone children.
Igor Murashkin617da162012-11-29 13:35:15 -0800886 merged_entries: A sequence of MergedEntry virtual nodes from entries
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800887 """
888 def __init__(self, name, parent):
889 self._name = name
890 self._parent = parent
891 self._namespaces = []
892 self._entries = []
893 self._leafs = []
894
895 @property
896 def namespaces(self):
897 return self._namespaces
898
899 @property
900 def entries(self):
901 return self._entries
902
Igor Murashkin617da162012-11-29 13:35:15 -0800903 @property
904 def merged_entries(self):
905 for i in self.entries:
906 yield i.merge()
907
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800908 def sort_children(self):
909 self._namespaces.sort(key=self._get_name())
910 self._entries.sort(key=self._get_name())
911
912 def _get_children(self):
913 for i in self.namespaces:
914 yield i
915 for i in self.entries:
916 yield i
917
Igor Murashkinaa133d32013-06-28 17:27:49 -0700918 def combine_children_by_name(self):
919 r"""
920 Combine multiple children with the same name into a single node.
921
922 Returns:
923 A new InnerNamespace where all of the children with the same name were
924 combined.
925
926 For example:
927
928 Given an InnerNamespace i:
929
930 i
931 / | \
932 a b c
933 | | |
934 d e f
935
936 a.name == "foo"
937 b.name == "foo"
938 c.name == "bar"
939
940 The returned InnerNamespace will look like this:
941
942 i'
943 / \
944 a' c'
945 / | |
946 d e f
947
948 Remarks:
949 This operation is not recursive. To combine the grandchildren and other
950 ancestors, call this method on the ancestor nodes.
951 """
952 return Kind._combine_children_by_name(self, new_type=type(self))
953
Igor Murashkin375cfd32012-12-03 13:55:33 -0800954class EnumValue(Node):
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800955 """
956 A class corresponding to a <value> element within an <enum> within an <entry>.
957
958 Attributes (Read-Only):
959 name: A string, e.g. 'ON' or 'OFF'
960 id: An optional numeric string, e.g. '0' or '0xFF'
Zhijun He7defc682015-05-22 17:04:15 -0700961 deprecated: A boolean, True if the enum should be deprecated.
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800962 optional: A boolean
Eino-Ville Talvalab4329162014-06-09 14:23:02 -0700963 hidden: A boolean, True if the enum should be hidden.
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -0700964 ndk_hidden: A boolean, True if the enum should be hidden in NDK
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800965 notes: A string describing the notes, or None.
Igor Murashkin375cfd32012-12-03 13:55:33 -0800966 parent: An edge to the parent, always an Enum instance.
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800967 """
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -0700968 def __init__(self, name, parent,
969 id=None, deprecated=False, optional=False, hidden=False, notes=None, ndk_hidden=False):
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800970 self._name = name # str, e.g. 'ON' or 'OFF'
971 self._id = id # int, e.g. '0'
Zhijun He7defc682015-05-22 17:04:15 -0700972 self._deprecated = deprecated # bool
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800973 self._optional = optional # bool
Eino-Ville Talvalab4329162014-06-09 14:23:02 -0700974 self._hidden = hidden # bool
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -0700975 self._ndk_hidden = ndk_hidden # bool
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800976 self._notes = notes # None or str
Igor Murashkin375cfd32012-12-03 13:55:33 -0800977 self._parent = parent
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800978
979 @property
980 def id(self):
981 return self._id
982
983 @property
Zhijun He7defc682015-05-22 17:04:15 -0700984 def deprecated(self):
985 return self._deprecated
986
987 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800988 def optional(self):
989 return self._optional
990
991 @property
Eino-Ville Talvalab4329162014-06-09 14:23:02 -0700992 def hidden(self):
993 return self._hidden
994
995 @property
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -0700996 def ndk_hidden(self):
997 return self._ndk_hidden
998
999 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001000 def notes(self):
1001 return self._notes
1002
Igor Murashkin375cfd32012-12-03 13:55:33 -08001003 def _get_children(self):
1004 return None
1005
1006class Enum(Node):
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001007 """
1008 A class corresponding to an <enum> element within an <entry>.
1009
1010 Attributes (Read-Only):
1011 parent: An edge to the parent, always an Entry instance.
1012 values: A sequence of EnumValue children.
Igor Murashkinaa133d32013-06-28 17:27:49 -07001013 has_values_with_id: A boolean representing if any of the children have a
1014 non-empty id property.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001015 """
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -07001016 def __init__(self, parent, values, ids={}, deprecateds=[],
1017 optionals=[], hiddens=[], notes={}, ndk_hiddens=[]):
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001018 self._values = \
Zhijun He7defc682015-05-22 17:04:15 -07001019 [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens, \
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -07001020 notes.get(val), val in ndk_hiddens) \
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001021 for val in values ]
1022
1023 self._parent = parent
Igor Murashkin375cfd32012-12-03 13:55:33 -08001024 self._name = None
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001025
1026 @property
1027 def values(self):
1028 return (i for i in self._values)
1029
Igor Murashkinaa133d32013-06-28 17:27:49 -07001030 @property
1031 def has_values_with_id(self):
1032 return bool(any(i for i in self.values if i.id))
1033
Igor Murashkin375cfd32012-12-03 13:55:33 -08001034 def _get_children(self):
1035 return (i for i in self._values)
1036
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001037class Entry(Node):
1038 """
1039 A node corresponding to an <entry> element.
1040
1041 Attributes (Read-Only):
1042 parent: An edge to the parent node, which is an InnerNamespace or Kind.
1043 name: The fully qualified name string, e.g. 'android.shading.mode'
1044 name_short: The name attribute from <entry name="mode">, e.g. mode
1045 type: The type attribute from <entry type="bar">
1046 kind: A string ('static', 'dynamic', 'controls') corresponding to the
1047 ancestor Kind#name
1048 container: The container attribute from <entry container="array">, or None.
1049 container_sizes: A sequence of size strings or None if container is None.
Igor Murashkinb556bc42012-12-04 16:07:21 -08001050 enum: An Enum instance if the enum attribute is true, None otherwise.
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001051 visibility: The visibility of this entry ('system', 'hidden', 'public')
1052 across the system. System entries are only visible in native code
1053 headers. Hidden entries are marked @hide in managed code, while
1054 public entries are visible in the Android SDK.
1055 applied_visibility: As visibility, but always valid, defaulting to 'system'
1056 if no visibility is given for an entry.
Yin-Chia Yehc6c24162016-04-02 16:30:30 -07001057 applied_ndk_visible: Always valid. Default is 'false'.
1058 Set to 'true' when the visibility implied entry is visible
1059 in NDK.
Igor Murashkin6c936c12014-05-13 14:51:49 -07001060 synthetic: The C-level visibility of this entry ('false', 'true').
1061 Synthetic entries will not be generated into the native metadata
1062 list of entries (in C code). In general a synthetic entry is
1063 glued together at the Java layer from multiple visibiltity=hidden
1064 entries.
Igor Murashkinca256272014-10-02 15:27:09 -07001065 hwlevel: The lowest hardware level at which the entry is guaranteed
1066 to be supported by the camera device. All devices with higher
1067 hwlevels will also include this entry. None means that the
1068 entry is optional on any hardware level.
Igor Murashkin6c936c12014-05-13 14:51:49 -07001069 deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1070 unreleased version this needs to be removed altogether. If applied
1071 to an entry from an older release, then this means the entry
1072 should be ignored by newer code.
Alex Rayef40ad62013-10-01 17:52:33 -07001073 optional: a bool representing the optional attribute, which denotes the entry
1074 is required for hardware level full devices, but optional for other
1075 hardware levels. None if not present.
1076 applied_optional: As optional but always valid, defaulting to False if no
1077 optional attribute is present.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001078 tuple_values: A sequence of strings describing the tuple values,
1079 None if container is not 'tuple'.
1080 description: A string description, or None.
1081 range: A string range, or None.
1082 units: A string units, or None.
1083 tags: A sequence of Tag nodes associated with this Entry.
1084 type_notes: A string describing notes for the type, or None.
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001085 typedef: A Typedef associated with this Entry, or None.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001086
1087 Remarks:
1088 Subclass Clone can be used interchangeable with an Entry,
1089 for when we don't care about the underlying type.
1090
1091 parent and tags edges are invalid until after Metadata#construct_graph
1092 has been invoked.
1093 """
1094 def __init__(self, **kwargs):
1095 """
1096 Instantiate a new Entry node.
1097
1098 Args:
1099 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1100 type: A string describing the type, e.g. 'int32'
1101 kind: A string describing the kind, e.g. 'static'
1102
1103 Args (if container):
1104 container: A string describing the container, e.g. 'array' or 'tuple'
1105 container_sizes: A list of string sizes if a container, or None otherwise
1106
1107 Args (if container is 'tuple'):
1108 tuple_values: A list of tuple values, e.g. ['width', 'height']
1109
Igor Murashkinb556bc42012-12-04 16:07:21 -08001110 Args (if the 'enum' attribute is true):
1111 enum: A boolean, True if this is an enum, False otherwise
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001112 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1113 enum_optionals: A list of optional enum values, e.g. ['OFF']
1114 enum_notes: A dictionary of value->notes strings.
1115 enum_ids: A dictionary of value->id strings.
1116
1117 Args (optional):
1118 description: A string with a description of the entry.
1119 range: A string with the range of the values of the entry, e.g. '>= 0'
1120 units: A string with the units of the values, e.g. 'inches'
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001121 details: A string with the detailed documentation for the entry
1122 hal_details: A string with the HAL implementation details for the entry
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001123 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1124 type_notes: A string with the notes for the type
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001125 visibility: A string describing the visibility, eg 'system', 'hidden',
1126 'public'
Igor Murashkin6c936c12014-05-13 14:51:49 -07001127 synthetic: A bool to mark whether this entry is visible only at the Java
1128 layer (True), or at both layers (False = default).
Igor Murashkinca256272014-10-02 15:27:09 -07001129 hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
Igor Murashkin6c936c12014-05-13 14:51:49 -07001130 deprecated: A bool to mark whether this is @Deprecated at the Java layer
1131 (default = False).
Alex Rayef40ad62013-10-01 17:52:33 -07001132 optional: A bool to mark whether optional for non-full hardware devices
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001133 typedef: A string corresponding to a typedef's name attribute.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001134 """
1135
1136 if kwargs.get('type') is None:
1137 print >> sys.stderr, "ERROR: Missing type for entry '%s' kind '%s'" \
1138 %(kwargs.get('name'), kwargs.get('kind'))
1139
1140 # Attributes are Read-Only, but edges may be mutated by
1141 # Metadata, particularly during construct_graph
1142
1143 self._name = kwargs['name']
1144 self._type = kwargs['type']
1145 self._kind = kwargs['kind'] # static, dynamic, or controls
1146
1147 self._init_common(**kwargs)
1148
1149 @property
1150 def type(self):
1151 return self._type
1152
1153 @property
1154 def kind(self):
1155 return self._kind
1156
1157 @property
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001158 def visibility(self):
1159 return self._visibility
1160
1161 @property
1162 def applied_visibility(self):
1163 return self._visibility or 'system'
1164
1165 @property
Yin-Chia Yehc6c24162016-04-02 16:30:30 -07001166 def applied_ndk_visible(self):
1167 if self._visibility in ("public", "ndk_public"):
1168 return "true"
1169 return "false"
1170
1171 @property
Igor Murashkin6c936c12014-05-13 14:51:49 -07001172 def synthetic(self):
1173 return self._synthetic
1174
1175 @property
Igor Murashkinca256272014-10-02 15:27:09 -07001176 def hwlevel(self):
1177 return self._hwlevel
1178
1179 @property
Igor Murashkin6c936c12014-05-13 14:51:49 -07001180 def deprecated(self):
1181 return self._deprecated
1182
Igor Murashkinca256272014-10-02 15:27:09 -07001183 # TODO: optional should just return hwlevel is None
Igor Murashkin6c936c12014-05-13 14:51:49 -07001184 @property
Alex Rayef40ad62013-10-01 17:52:33 -07001185 def optional(self):
1186 return self._optional
1187
1188 @property
1189 def applied_optional(self):
1190 return self._optional or False
1191
1192 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001193 def name_short(self):
1194 return self.get_name_minimal()
1195
1196 @property
1197 def container(self):
1198 return self._container
1199
1200 @property
1201 def container_sizes(self):
1202 if self._container_sizes is None:
1203 return None
1204 else:
1205 return (i for i in self._container_sizes)
1206
1207 @property
1208 def tuple_values(self):
1209 if self._tuple_values is None:
1210 return None
1211 else:
1212 return (i for i in self._tuple_values)
1213
1214 @property
1215 def description(self):
1216 return self._description
1217
1218 @property
1219 def range(self):
1220 return self._range
1221
1222 @property
1223 def units(self):
1224 return self._units
1225
1226 @property
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001227 def details(self):
1228 return self._details
1229
1230 @property
1231 def hal_details(self):
1232 return self._hal_details
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001233
1234 @property
1235 def tags(self):
1236 if self._tags is None:
1237 return None
1238 else:
1239 return (i for i in self._tags)
1240
1241 @property
1242 def type_notes(self):
1243 return self._type_notes
1244
1245 @property
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001246 def typedef(self):
1247 return self._typedef
1248
1249 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001250 def enum(self):
1251 return self._enum
1252
1253 def _get_children(self):
Igor Murashkin375cfd32012-12-03 13:55:33 -08001254 if self.enum:
1255 yield self.enum
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001256
1257 def sort_children(self):
1258 return None
1259
1260 def is_clone(self):
1261 """
1262 Whether or not this is a Clone instance.
1263
1264 Returns:
1265 False
1266 """
1267 return False
1268
1269 def _init_common(self, **kwargs):
1270
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001271 self._parent = None # filled in by Metadata::_construct_entries
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001272
1273 self._container = kwargs.get('container')
1274 self._container_sizes = kwargs.get('container_sizes')
1275
1276 # access these via the 'enum' prop
1277 enum_values = kwargs.get('enum_values')
Zhijun He7defc682015-05-22 17:04:15 -07001278 enum_deprecateds = kwargs.get('enum_deprecateds')
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001279 enum_optionals = kwargs.get('enum_optionals')
Eino-Ville Talvalab4329162014-06-09 14:23:02 -07001280 enum_hiddens = kwargs.get('enum_hiddens')
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -07001281 enum_ndk_hiddens = kwargs.get('enum_ndk_hiddens')
Igor Murashkinaa133d32013-06-28 17:27:49 -07001282 enum_notes = kwargs.get('enum_notes') # { value => notes }
1283 enum_ids = kwargs.get('enum_ids') # { value => notes }
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001284 self._tuple_values = kwargs.get('tuple_values')
1285
1286 self._description = kwargs.get('description')
1287 self._range = kwargs.get('range')
1288 self._units = kwargs.get('units')
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001289 self._details = kwargs.get('details')
1290 self._hal_details = kwargs.get('hal_details')
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001291
1292 self._tag_ids = kwargs.get('tag_ids', [])
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001293 self._tags = None # Filled in by Metadata::_construct_tags
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001294
1295 self._type_notes = kwargs.get('type_notes')
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001296 self._type_name = kwargs.get('type_name')
1297 self._typedef = None # Filled in by Metadata::_construct_types
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001298
Igor Murashkinb556bc42012-12-04 16:07:21 -08001299 if kwargs.get('enum', False):
Zhijun He7defc682015-05-22 17:04:15 -07001300 self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -07001301 enum_hiddens, enum_notes, enum_ndk_hiddens)
Igor Murashkin617da162012-11-29 13:35:15 -08001302 else:
1303 self._enum = None
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001304
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001305 self._visibility = kwargs.get('visibility')
Igor Murashkin6c936c12014-05-13 14:51:49 -07001306 self._synthetic = kwargs.get('synthetic', False)
Igor Murashkinca256272014-10-02 15:27:09 -07001307 self._hwlevel = kwargs.get('hwlevel')
Igor Murashkin6c936c12014-05-13 14:51:49 -07001308 self._deprecated = kwargs.get('deprecated', False)
Alex Rayef40ad62013-10-01 17:52:33 -07001309 self._optional = kwargs.get('optional')
Yin-Chia Yehc6c24162016-04-02 16:30:30 -07001310 self._ndk_visible = kwargs.get('ndk_visible')
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001311
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001312 self._property_keys = kwargs
1313
Igor Murashkin617da162012-11-29 13:35:15 -08001314 def merge(self):
1315 """
1316 Copy the attributes into a new entry, merging it with the target entry
1317 if it's a clone.
1318 """
1319 return MergedEntry(self)
1320
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001321 # Helpers for accessing less than the fully qualified name
1322
1323 def get_name_as_list(self):
1324 """
1325 Returns the name as a list split by a period.
1326
1327 For example:
1328 entry.name is 'android.lens.info.shading'
1329 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1330 """
1331 return self.name.split(".")
1332
1333 def get_inner_namespace_list(self):
1334 """
1335 Returns the inner namespace part of the name as a list
1336
1337 For example:
1338 entry.name is 'android.lens.info.shading'
1339 entry.get_inner_namespace_list() == ['info']
1340 """
1341 return self.get_name_as_list()[2:-1]
1342
1343 def get_outer_namespace(self):
1344 """
1345 Returns the outer namespace as a string.
1346
1347 For example:
1348 entry.name is 'android.lens.info.shading'
1349 entry.get_outer_namespace() == 'android'
1350
1351 Remarks:
1352 Since outer namespaces are non-recursive,
1353 and each entry has one, this does not need to be a list.
1354 """
1355 return self.get_name_as_list()[0]
1356
1357 def get_section(self):
1358 """
1359 Returns the section as a string.
1360
1361 For example:
1362 entry.name is 'android.lens.info.shading'
1363 entry.get_section() == ''
1364
1365 Remarks:
1366 Since outer namespaces are non-recursive,
1367 and each entry has one, this does not need to be a list.
1368 """
1369 return self.get_name_as_list()[1]
1370
1371 def get_name_minimal(self):
1372 """
1373 Returns only the last component of the fully qualified name as a string.
1374
1375 For example:
1376 entry.name is 'android.lens.info.shading'
1377 entry.get_name_minimal() == 'shading'
1378
1379 Remarks:
1380 entry.name_short it an alias for this
1381 """
1382 return self.get_name_as_list()[-1]
1383
Igor Murashkin7b9a2dc2012-11-21 14:23:24 -08001384 def get_path_without_name(self):
1385 """
1386 Returns a string path to the entry, with the name component excluded.
1387
1388 For example:
1389 entry.name is 'android.lens.info.shading'
1390 entry.get_path_without_name() == 'android.lens.info'
1391 """
1392 return ".".join(self.get_name_as_list()[0:-1])
1393
1394
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001395class Clone(Entry):
1396 """
1397 A Node corresponding to a <clone> element. It has all the attributes of an
1398 <entry> element (Entry) plus the additions specified below.
1399
1400 Attributes (Read-Only):
1401 entry: an edge to an Entry object that this targets
1402 target_kind: A string describing the kind of the target entry.
1403 name: a string of the name, same as entry.name
1404 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1405 for the <clone> element.
1406 type: always None, since a clone cannot override the type.
1407 """
1408 def __init__(self, entry=None, **kwargs):
1409 """
1410 Instantiate a new Clone node.
1411
1412 Args:
1413 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1414 type: A string describing the type, e.g. 'int32'
1415 kind: A string describing the kind, e.g. 'static'
1416 target_kind: A string for the kind of the target entry, e.g. 'dynamic'
1417
1418 Args (if container):
1419 container: A string describing the container, e.g. 'array' or 'tuple'
1420 container_sizes: A list of string sizes if a container, or None otherwise
1421
1422 Args (if container is 'tuple'):
1423 tuple_values: A list of tuple values, e.g. ['width', 'height']
1424
Igor Murashkinb556bc42012-12-04 16:07:21 -08001425 Args (if the 'enum' attribute is true):
1426 enum: A boolean, True if this is an enum, False otherwise
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001427 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1428 enum_optionals: A list of optional enum values, e.g. ['OFF']
1429 enum_notes: A dictionary of value->notes strings.
1430 enum_ids: A dictionary of value->id strings.
1431
1432 Args (optional):
1433 entry: An edge to the corresponding target Entry.
1434 description: A string with a description of the entry.
1435 range: A string with the range of the values of the entry, e.g. '>= 0'
1436 units: A string with the units of the values, e.g. 'inches'
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001437 details: A string with the detailed documentation for the entry
1438 hal_details: A string with the HAL implementation details for the entry
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001439 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1440 type_notes: A string with the notes for the type
1441
1442 Remarks:
1443 Note that type is not specified since it has to be the same as the
1444 entry.type.
1445 """
Igor Murashkinaa133d32013-06-28 17:27:49 -07001446 self._entry = entry # Entry object
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001447 self._target_kind = kwargs['target_kind']
Igor Murashkinaa133d32013-06-28 17:27:49 -07001448 self._name = kwargs['name'] # same as entry.name
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001449 self._kind = kwargs['kind']
1450
1451 # illegal to override the type, it should be the same as the entry
1452 self._type = None
1453 # the rest of the kwargs are optional
1454 # can be used to override the regular entry data
1455 self._init_common(**kwargs)
1456
1457 @property
1458 def entry(self):
1459 return self._entry
1460
1461 @property
1462 def target_kind(self):
1463 return self._target_kind
1464
1465 def is_clone(self):
1466 """
1467 Whether or not this is a Clone instance.
1468
1469 Returns:
1470 True
1471 """
1472 return True
1473
Igor Murashkin617da162012-11-29 13:35:15 -08001474class MergedEntry(Entry):
1475 """
1476 A MergedEntry has all the attributes of a Clone and its target Entry merged
1477 together.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001478
Igor Murashkin617da162012-11-29 13:35:15 -08001479 Remarks:
1480 Useful when we want to 'unfold' a clone into a real entry by copying out
1481 the target entry data. In this case we don't care about distinguishing
1482 a clone vs an entry.
1483 """
1484 def __init__(self, entry):
1485 """
1486 Create a new instance of MergedEntry.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001487
Igor Murashkin617da162012-11-29 13:35:15 -08001488 Args:
1489 entry: An Entry or Clone instance
1490 """
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001491 props_distinct = ['description', 'units', 'range', 'details',
1492 'hal_details', 'tags', 'kind']
Igor Murashkin617da162012-11-29 13:35:15 -08001493
1494 for p in props_distinct:
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001495 p = '_' + p
Igor Murashkin617da162012-11-29 13:35:15 -08001496 if entry.is_clone():
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001497 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
Igor Murashkin617da162012-11-29 13:35:15 -08001498 else:
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001499 setattr(self, p, getattr(entry, p))
Igor Murashkin617da162012-11-29 13:35:15 -08001500
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001501 props_common = ['parent', 'name', 'container',
Igor Murashkin617da162012-11-29 13:35:15 -08001502 'container_sizes', 'enum',
1503 'tuple_values',
1504 'type',
1505 'type_notes',
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001506 'visibility',
Yin-Chia Yehc6c24162016-04-02 16:30:30 -07001507 'ndk_visible',
Igor Murashkin6c936c12014-05-13 14:51:49 -07001508 'synthetic',
Igor Murashkinca256272014-10-02 15:27:09 -07001509 'hwlevel',
Igor Murashkin6c936c12014-05-13 14:51:49 -07001510 'deprecated',
Alex Rayef40ad62013-10-01 17:52:33 -07001511 'optional',
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001512 'typedef'
Igor Murashkin617da162012-11-29 13:35:15 -08001513 ]
1514
1515 for p in props_common:
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001516 p = '_' + p
Igor Murashkin617da162012-11-29 13:35:15 -08001517 if entry.is_clone():
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001518 setattr(self, p, getattr(entry.entry, p))
Igor Murashkin617da162012-11-29 13:35:15 -08001519 else:
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001520 setattr(self, p, getattr(entry, p))