blob: 66a734d157da159869de8224861b48cb4e59f9fd [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
Eino-Ville Talvala0d404952017-11-10 15:13:04 -080021metadata_definitions.xml file.
Igor Murashkin77b63ca2012-11-09 16:15:02 -080022
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.
Eino-Ville Talvala0d404952017-11-10 15:13:04 -0800966 sdk_notes: A string describing extra notes for public SDK only
967 ndk_notes: A string describing extra notes for public NDK only
Igor Murashkin375cfd32012-12-03 13:55:33 -0800968 parent: An edge to the parent, always an Enum instance.
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800969 """
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -0700970 def __init__(self, name, parent,
Eino-Ville Talvala0d404952017-11-10 15:13:04 -0800971 id=None, deprecated=False, optional=False, hidden=False, notes=None, sdk_notes=None, ndk_notes=None, ndk_hidden=False):
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800972 self._name = name # str, e.g. 'ON' or 'OFF'
973 self._id = id # int, e.g. '0'
Zhijun He7defc682015-05-22 17:04:15 -0700974 self._deprecated = deprecated # bool
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800975 self._optional = optional # bool
Eino-Ville Talvalab4329162014-06-09 14:23:02 -0700976 self._hidden = hidden # bool
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -0700977 self._ndk_hidden = ndk_hidden # bool
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800978 self._notes = notes # None or str
Eino-Ville Talvala0d404952017-11-10 15:13:04 -0800979 self._sdk_notes = sdk_notes # None or str
980 self._ndk_notes = ndk_notes # None or str
Igor Murashkin375cfd32012-12-03 13:55:33 -0800981 self._parent = parent
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800982
983 @property
984 def id(self):
985 return self._id
986
987 @property
Zhijun He7defc682015-05-22 17:04:15 -0700988 def deprecated(self):
989 return self._deprecated
990
991 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -0800992 def optional(self):
993 return self._optional
994
995 @property
Eino-Ville Talvalab4329162014-06-09 14:23:02 -0700996 def hidden(self):
997 return self._hidden
998
999 @property
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -07001000 def ndk_hidden(self):
1001 return self._ndk_hidden
1002
1003 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001004 def notes(self):
1005 return self._notes
1006
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001007 @property
1008 def sdk_notes(self):
1009 return self._sdk_notes
1010
1011 @property
1012 def ndk_notes(self):
1013 return self._ndk_notes
1014
Igor Murashkin375cfd32012-12-03 13:55:33 -08001015 def _get_children(self):
1016 return None
1017
1018class Enum(Node):
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001019 """
1020 A class corresponding to an <enum> element within an <entry>.
1021
1022 Attributes (Read-Only):
1023 parent: An edge to the parent, always an Entry instance.
1024 values: A sequence of EnumValue children.
Igor Murashkinaa133d32013-06-28 17:27:49 -07001025 has_values_with_id: A boolean representing if any of the children have a
1026 non-empty id property.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001027 """
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -07001028 def __init__(self, parent, values, ids={}, deprecateds=[],
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001029 optionals=[], hiddens=[], notes={}, sdk_notes={}, ndk_notes={}, ndk_hiddens=[]):
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001030 self._values = \
Zhijun He7defc682015-05-22 17:04:15 -07001031 [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens, \
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001032 notes.get(val), sdk_notes.get(val), ndk_notes.get(val), val in ndk_hiddens) \
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001033 for val in values ]
1034
1035 self._parent = parent
Igor Murashkin375cfd32012-12-03 13:55:33 -08001036 self._name = None
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001037
1038 @property
1039 def values(self):
1040 return (i for i in self._values)
1041
Igor Murashkinaa133d32013-06-28 17:27:49 -07001042 @property
1043 def has_values_with_id(self):
1044 return bool(any(i for i in self.values if i.id))
1045
Igor Murashkin375cfd32012-12-03 13:55:33 -08001046 def _get_children(self):
1047 return (i for i in self._values)
1048
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001049class Entry(Node):
1050 """
1051 A node corresponding to an <entry> element.
1052
1053 Attributes (Read-Only):
1054 parent: An edge to the parent node, which is an InnerNamespace or Kind.
1055 name: The fully qualified name string, e.g. 'android.shading.mode'
1056 name_short: The name attribute from <entry name="mode">, e.g. mode
1057 type: The type attribute from <entry type="bar">
1058 kind: A string ('static', 'dynamic', 'controls') corresponding to the
1059 ancestor Kind#name
1060 container: The container attribute from <entry container="array">, or None.
1061 container_sizes: A sequence of size strings or None if container is None.
Igor Murashkinb556bc42012-12-04 16:07:21 -08001062 enum: An Enum instance if the enum attribute is true, None otherwise.
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001063 visibility: The visibility of this entry ('system', 'hidden', 'public')
1064 across the system. System entries are only visible in native code
1065 headers. Hidden entries are marked @hide in managed code, while
1066 public entries are visible in the Android SDK.
1067 applied_visibility: As visibility, but always valid, defaulting to 'system'
1068 if no visibility is given for an entry.
Yin-Chia Yehc6c24162016-04-02 16:30:30 -07001069 applied_ndk_visible: Always valid. Default is 'false'.
1070 Set to 'true' when the visibility implied entry is visible
1071 in NDK.
Igor Murashkin6c936c12014-05-13 14:51:49 -07001072 synthetic: The C-level visibility of this entry ('false', 'true').
1073 Synthetic entries will not be generated into the native metadata
1074 list of entries (in C code). In general a synthetic entry is
1075 glued together at the Java layer from multiple visibiltity=hidden
1076 entries.
Igor Murashkinca256272014-10-02 15:27:09 -07001077 hwlevel: The lowest hardware level at which the entry is guaranteed
1078 to be supported by the camera device. All devices with higher
1079 hwlevels will also include this entry. None means that the
1080 entry is optional on any hardware level.
Igor Murashkin6c936c12014-05-13 14:51:49 -07001081 deprecated: Marks an entry as @Deprecated in the Java layer; if within an
1082 unreleased version this needs to be removed altogether. If applied
1083 to an entry from an older release, then this means the entry
1084 should be ignored by newer code.
Alex Rayef40ad62013-10-01 17:52:33 -07001085 optional: a bool representing the optional attribute, which denotes the entry
1086 is required for hardware level full devices, but optional for other
1087 hardware levels. None if not present.
1088 applied_optional: As optional but always valid, defaulting to False if no
1089 optional attribute is present.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001090 tuple_values: A sequence of strings describing the tuple values,
1091 None if container is not 'tuple'.
1092 description: A string description, or None.
1093 range: A string range, or None.
1094 units: A string units, or None.
1095 tags: A sequence of Tag nodes associated with this Entry.
1096 type_notes: A string describing notes for the type, or None.
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001097 typedef: A Typedef associated with this Entry, or None.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001098
1099 Remarks:
1100 Subclass Clone can be used interchangeable with an Entry,
1101 for when we don't care about the underlying type.
1102
1103 parent and tags edges are invalid until after Metadata#construct_graph
1104 has been invoked.
1105 """
1106 def __init__(self, **kwargs):
1107 """
1108 Instantiate a new Entry node.
1109
1110 Args:
1111 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1112 type: A string describing the type, e.g. 'int32'
1113 kind: A string describing the kind, e.g. 'static'
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001114 hal_version: A string for the initial HIDL HAL metadata version this entry
1115 was added in
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001116
1117 Args (if container):
1118 container: A string describing the container, e.g. 'array' or 'tuple'
1119 container_sizes: A list of string sizes if a container, or None otherwise
1120
1121 Args (if container is 'tuple'):
1122 tuple_values: A list of tuple values, e.g. ['width', 'height']
1123
Igor Murashkinb556bc42012-12-04 16:07:21 -08001124 Args (if the 'enum' attribute is true):
1125 enum: A boolean, True if this is an enum, False otherwise
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001126 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1127 enum_optionals: A list of optional enum values, e.g. ['OFF']
1128 enum_notes: A dictionary of value->notes strings.
1129 enum_ids: A dictionary of value->id strings.
1130
1131 Args (optional):
1132 description: A string with a description of the entry.
1133 range: A string with the range of the values of the entry, e.g. '>= 0'
1134 units: A string with the units of the values, e.g. 'inches'
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001135 details: A string with the detailed documentation for the entry
1136 hal_details: A string with the HAL implementation details for the entry
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001137 ndk_details: A string with the extra NDK API documentation for the entry=
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001138 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1139 type_notes: A string with the notes for the type
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001140 visibility: A string describing the visibility, eg 'system', 'hidden',
1141 'public'
Igor Murashkin6c936c12014-05-13 14:51:49 -07001142 synthetic: A bool to mark whether this entry is visible only at the Java
1143 layer (True), or at both layers (False = default).
Igor Murashkinca256272014-10-02 15:27:09 -07001144 hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full')
Igor Murashkin6c936c12014-05-13 14:51:49 -07001145 deprecated: A bool to mark whether this is @Deprecated at the Java layer
1146 (default = False).
Alex Rayef40ad62013-10-01 17:52:33 -07001147 optional: A bool to mark whether optional for non-full hardware devices
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001148 typedef: A string corresponding to a typedef's name attribute.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001149 """
1150
1151 if kwargs.get('type') is None:
1152 print >> sys.stderr, "ERROR: Missing type for entry '%s' kind '%s'" \
1153 %(kwargs.get('name'), kwargs.get('kind'))
1154
1155 # Attributes are Read-Only, but edges may be mutated by
1156 # Metadata, particularly during construct_graph
1157
1158 self._name = kwargs['name']
1159 self._type = kwargs['type']
1160 self._kind = kwargs['kind'] # static, dynamic, or controls
1161
1162 self._init_common(**kwargs)
1163
1164 @property
1165 def type(self):
1166 return self._type
1167
1168 @property
1169 def kind(self):
1170 return self._kind
1171
1172 @property
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001173 def hal_version(self):
1174 return self._hal_version
1175
1176 @property
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001177 def visibility(self):
1178 return self._visibility
1179
1180 @property
1181 def applied_visibility(self):
1182 return self._visibility or 'system'
1183
1184 @property
Yin-Chia Yehc6c24162016-04-02 16:30:30 -07001185 def applied_ndk_visible(self):
1186 if self._visibility in ("public", "ndk_public"):
1187 return "true"
1188 return "false"
1189
1190 @property
Igor Murashkin6c936c12014-05-13 14:51:49 -07001191 def synthetic(self):
1192 return self._synthetic
1193
1194 @property
Igor Murashkinca256272014-10-02 15:27:09 -07001195 def hwlevel(self):
1196 return self._hwlevel
1197
1198 @property
Igor Murashkin6c936c12014-05-13 14:51:49 -07001199 def deprecated(self):
1200 return self._deprecated
1201
Igor Murashkinca256272014-10-02 15:27:09 -07001202 # TODO: optional should just return hwlevel is None
Igor Murashkin6c936c12014-05-13 14:51:49 -07001203 @property
Alex Rayef40ad62013-10-01 17:52:33 -07001204 def optional(self):
1205 return self._optional
1206
1207 @property
1208 def applied_optional(self):
1209 return self._optional or False
1210
1211 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001212 def name_short(self):
1213 return self.get_name_minimal()
1214
1215 @property
1216 def container(self):
1217 return self._container
1218
1219 @property
1220 def container_sizes(self):
1221 if self._container_sizes is None:
1222 return None
1223 else:
1224 return (i for i in self._container_sizes)
1225
1226 @property
1227 def tuple_values(self):
1228 if self._tuple_values is None:
1229 return None
1230 else:
1231 return (i for i in self._tuple_values)
1232
1233 @property
1234 def description(self):
1235 return self._description
1236
1237 @property
1238 def range(self):
1239 return self._range
1240
1241 @property
1242 def units(self):
1243 return self._units
1244
1245 @property
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001246 def details(self):
1247 return self._details
1248
1249 @property
1250 def hal_details(self):
1251 return self._hal_details
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001252
1253 @property
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001254 def ndk_details(self):
1255 return self._ndk_details
1256
1257 @property
1258 def applied_ndk_details(self):
1259 return (self._details or "") + (self._ndk_details or "")
1260
1261 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001262 def tags(self):
1263 if self._tags is None:
1264 return None
1265 else:
1266 return (i for i in self._tags)
1267
1268 @property
1269 def type_notes(self):
1270 return self._type_notes
1271
1272 @property
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001273 def typedef(self):
1274 return self._typedef
1275
1276 @property
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001277 def enum(self):
1278 return self._enum
1279
1280 def _get_children(self):
Igor Murashkin375cfd32012-12-03 13:55:33 -08001281 if self.enum:
1282 yield self.enum
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001283
1284 def sort_children(self):
1285 return None
1286
1287 def is_clone(self):
1288 """
1289 Whether or not this is a Clone instance.
1290
1291 Returns:
1292 False
1293 """
1294 return False
1295
1296 def _init_common(self, **kwargs):
1297
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001298 self._parent = None # filled in by Metadata::_construct_entries
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001299
1300 self._container = kwargs.get('container')
1301 self._container_sizes = kwargs.get('container_sizes')
1302
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001303 self._hal_version = kwargs.get('hal_version')
1304 if self._hal_version is None:
1305 self._hal_version = '3.2'
1306
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001307 # access these via the 'enum' prop
1308 enum_values = kwargs.get('enum_values')
Zhijun He7defc682015-05-22 17:04:15 -07001309 enum_deprecateds = kwargs.get('enum_deprecateds')
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001310 enum_optionals = kwargs.get('enum_optionals')
Eino-Ville Talvalab4329162014-06-09 14:23:02 -07001311 enum_hiddens = kwargs.get('enum_hiddens')
Yin-Chia Yeh3ba998e2016-04-06 11:58:41 -07001312 enum_ndk_hiddens = kwargs.get('enum_ndk_hiddens')
Igor Murashkinaa133d32013-06-28 17:27:49 -07001313 enum_notes = kwargs.get('enum_notes') # { value => notes }
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001314 enum_sdk_notes = kwargs.get('enum_sdk_notes') # { value => sdk_notes }
1315 enum_ndk_notes = kwargs.get('enum_ndk_notes') # { value => ndk_notes }
Igor Murashkinaa133d32013-06-28 17:27:49 -07001316 enum_ids = kwargs.get('enum_ids') # { value => notes }
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001317 self._tuple_values = kwargs.get('tuple_values')
1318
1319 self._description = kwargs.get('description')
1320 self._range = kwargs.get('range')
1321 self._units = kwargs.get('units')
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001322 self._details = kwargs.get('details')
1323 self._hal_details = kwargs.get('hal_details')
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001324 self._ndk_details = kwargs.get('ndk_details')
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001325
1326 self._tag_ids = kwargs.get('tag_ids', [])
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001327 self._tags = None # Filled in by Metadata::_construct_tags
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001328
1329 self._type_notes = kwargs.get('type_notes')
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001330 self._type_name = kwargs.get('type_name')
1331 self._typedef = None # Filled in by Metadata::_construct_types
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001332
Igor Murashkinb556bc42012-12-04 16:07:21 -08001333 if kwargs.get('enum', False):
Zhijun He7defc682015-05-22 17:04:15 -07001334 self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001335 enum_hiddens, enum_notes, enum_sdk_notes, enum_ndk_notes, enum_ndk_hiddens)
Igor Murashkin617da162012-11-29 13:35:15 -08001336 else:
1337 self._enum = None
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001338
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001339 self._visibility = kwargs.get('visibility')
Igor Murashkin6c936c12014-05-13 14:51:49 -07001340 self._synthetic = kwargs.get('synthetic', False)
Igor Murashkinca256272014-10-02 15:27:09 -07001341 self._hwlevel = kwargs.get('hwlevel')
Igor Murashkin6c936c12014-05-13 14:51:49 -07001342 self._deprecated = kwargs.get('deprecated', False)
Alex Rayef40ad62013-10-01 17:52:33 -07001343 self._optional = kwargs.get('optional')
Yin-Chia Yehc6c24162016-04-02 16:30:30 -07001344 self._ndk_visible = kwargs.get('ndk_visible')
Eino-Ville Talvalaf384f0a2013-07-12 17:02:27 -07001345
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001346 self._property_keys = kwargs
1347
Igor Murashkin617da162012-11-29 13:35:15 -08001348 def merge(self):
1349 """
1350 Copy the attributes into a new entry, merging it with the target entry
1351 if it's a clone.
1352 """
1353 return MergedEntry(self)
1354
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001355 # Helpers for accessing less than the fully qualified name
1356
1357 def get_name_as_list(self):
1358 """
1359 Returns the name as a list split by a period.
1360
1361 For example:
1362 entry.name is 'android.lens.info.shading'
1363 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading']
1364 """
1365 return self.name.split(".")
1366
1367 def get_inner_namespace_list(self):
1368 """
1369 Returns the inner namespace part of the name as a list
1370
1371 For example:
1372 entry.name is 'android.lens.info.shading'
1373 entry.get_inner_namespace_list() == ['info']
1374 """
1375 return self.get_name_as_list()[2:-1]
1376
1377 def get_outer_namespace(self):
1378 """
1379 Returns the outer namespace as a string.
1380
1381 For example:
1382 entry.name is 'android.lens.info.shading'
1383 entry.get_outer_namespace() == 'android'
1384
1385 Remarks:
1386 Since outer namespaces are non-recursive,
1387 and each entry has one, this does not need to be a list.
1388 """
1389 return self.get_name_as_list()[0]
1390
1391 def get_section(self):
1392 """
1393 Returns the section as a string.
1394
1395 For example:
1396 entry.name is 'android.lens.info.shading'
1397 entry.get_section() == ''
1398
1399 Remarks:
1400 Since outer namespaces are non-recursive,
1401 and each entry has one, this does not need to be a list.
1402 """
1403 return self.get_name_as_list()[1]
1404
1405 def get_name_minimal(self):
1406 """
1407 Returns only the last component of the fully qualified name as a string.
1408
1409 For example:
1410 entry.name is 'android.lens.info.shading'
1411 entry.get_name_minimal() == 'shading'
1412
1413 Remarks:
1414 entry.name_short it an alias for this
1415 """
1416 return self.get_name_as_list()[-1]
1417
Igor Murashkin7b9a2dc2012-11-21 14:23:24 -08001418 def get_path_without_name(self):
1419 """
1420 Returns a string path to the entry, with the name component excluded.
1421
1422 For example:
1423 entry.name is 'android.lens.info.shading'
1424 entry.get_path_without_name() == 'android.lens.info'
1425 """
1426 return ".".join(self.get_name_as_list()[0:-1])
1427
1428
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001429class Clone(Entry):
1430 """
1431 A Node corresponding to a <clone> element. It has all the attributes of an
1432 <entry> element (Entry) plus the additions specified below.
1433
1434 Attributes (Read-Only):
1435 entry: an edge to an Entry object that this targets
1436 target_kind: A string describing the kind of the target entry.
1437 name: a string of the name, same as entry.name
1438 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic'
1439 for the <clone> element.
1440 type: always None, since a clone cannot override the type.
1441 """
1442 def __init__(self, entry=None, **kwargs):
1443 """
1444 Instantiate a new Clone node.
1445
1446 Args:
1447 name: A string with the fully qualified name, e.g. 'android.shading.mode'
1448 type: A string describing the type, e.g. 'int32'
1449 kind: A string describing the kind, e.g. 'static'
1450 target_kind: A string for the kind of the target entry, e.g. 'dynamic'
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001451 hal_version: A string for the initial HIDL HAL metadata version this entry
1452 was added in
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001453
1454 Args (if container):
1455 container: A string describing the container, e.g. 'array' or 'tuple'
1456 container_sizes: A list of string sizes if a container, or None otherwise
1457
1458 Args (if container is 'tuple'):
1459 tuple_values: A list of tuple values, e.g. ['width', 'height']
1460
Igor Murashkinb556bc42012-12-04 16:07:21 -08001461 Args (if the 'enum' attribute is true):
1462 enum: A boolean, True if this is an enum, False otherwise
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001463 enum_values: A list of value strings, e.g. ['ON', 'OFF']
1464 enum_optionals: A list of optional enum values, e.g. ['OFF']
1465 enum_notes: A dictionary of value->notes strings.
1466 enum_ids: A dictionary of value->id strings.
1467
1468 Args (optional):
1469 entry: An edge to the corresponding target Entry.
1470 description: A string with a description of the entry.
1471 range: A string with the range of the values of the entry, e.g. '>= 0'
1472 units: A string with the units of the values, e.g. 'inches'
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001473 details: A string with the detailed documentation for the entry
1474 hal_details: A string with the HAL implementation details for the entry
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001475 ndk_details: A string with the extra NDK documentation for the entry
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001476 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1']
1477 type_notes: A string with the notes for the type
1478
1479 Remarks:
1480 Note that type is not specified since it has to be the same as the
1481 entry.type.
1482 """
Igor Murashkinaa133d32013-06-28 17:27:49 -07001483 self._entry = entry # Entry object
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001484 self._target_kind = kwargs['target_kind']
Igor Murashkinaa133d32013-06-28 17:27:49 -07001485 self._name = kwargs['name'] # same as entry.name
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001486 self._kind = kwargs['kind']
1487
1488 # illegal to override the type, it should be the same as the entry
1489 self._type = None
1490 # the rest of the kwargs are optional
1491 # can be used to override the regular entry data
1492 self._init_common(**kwargs)
1493
1494 @property
1495 def entry(self):
1496 return self._entry
1497
1498 @property
1499 def target_kind(self):
1500 return self._target_kind
1501
1502 def is_clone(self):
1503 """
1504 Whether or not this is a Clone instance.
1505
1506 Returns:
1507 True
1508 """
1509 return True
1510
Igor Murashkin617da162012-11-29 13:35:15 -08001511class MergedEntry(Entry):
1512 """
1513 A MergedEntry has all the attributes of a Clone and its target Entry merged
1514 together.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001515
Igor Murashkin617da162012-11-29 13:35:15 -08001516 Remarks:
1517 Useful when we want to 'unfold' a clone into a real entry by copying out
1518 the target entry data. In this case we don't care about distinguishing
1519 a clone vs an entry.
1520 """
1521 def __init__(self, entry):
1522 """
1523 Create a new instance of MergedEntry.
Igor Murashkin77b63ca2012-11-09 16:15:02 -08001524
Igor Murashkin617da162012-11-29 13:35:15 -08001525 Args:
1526 entry: An Entry or Clone instance
1527 """
Eino-Ville Talvalaa5b73c22013-12-27 13:50:19 -08001528 props_distinct = ['description', 'units', 'range', 'details',
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001529 'hal_details', 'ndk_details', 'tags', 'kind']
Igor Murashkin617da162012-11-29 13:35:15 -08001530
1531 for p in props_distinct:
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001532 p = '_' + p
Igor Murashkin617da162012-11-29 13:35:15 -08001533 if entry.is_clone():
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001534 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p))
Igor Murashkin617da162012-11-29 13:35:15 -08001535 else:
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001536 setattr(self, p, getattr(entry, p))
Igor Murashkin617da162012-11-29 13:35:15 -08001537
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001538 props_common = ['parent', 'name', 'container',
Igor Murashkin617da162012-11-29 13:35:15 -08001539 'container_sizes', 'enum',
1540 'tuple_values',
1541 'type',
1542 'type_notes',
Igor Murashkinb8dc8812013-07-17 16:29:34 -07001543 'visibility',
Yin-Chia Yehc6c24162016-04-02 16:30:30 -07001544 'ndk_visible',
Igor Murashkin6c936c12014-05-13 14:51:49 -07001545 'synthetic',
Igor Murashkinca256272014-10-02 15:27:09 -07001546 'hwlevel',
Igor Murashkin6c936c12014-05-13 14:51:49 -07001547 'deprecated',
Alex Rayef40ad62013-10-01 17:52:33 -07001548 'optional',
Eino-Ville Talvala0d404952017-11-10 15:13:04 -08001549 'typedef',
1550 'hal_version'
Igor Murashkin617da162012-11-29 13:35:15 -08001551 ]
1552
1553 for p in props_common:
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001554 p = '_' + p
Igor Murashkin617da162012-11-29 13:35:15 -08001555 if entry.is_clone():
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001556 setattr(self, p, getattr(entry.entry, p))
Igor Murashkin617da162012-11-29 13:35:15 -08001557 else:
Igor Murashkinbaacf9a2012-12-05 14:49:11 -08001558 setattr(self, p, getattr(entry, p))