1#!/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_definitions.xml file. 22 23 Node: Base class for most nodes. 24 Entry: A node corresponding to <entry> elements. 25 Clone: A node corresponding to <clone> elements. 26 MergedEntry: A node corresponding to either <entry> or <clone> elements. 27 Kind: A node corresponding to <dynamic>, <static>, <controls> elements. 28 InnerNamespace: A node corresponding to a <namespace> nested under a <kind>. 29 OuterNamespace: A node corresponding to a <namespace> with <kind> children. 30 Section: A node corresponding to a <section> element. 31 Enum: A class corresponding an <enum> element within an <entry> 32 EnumValue: A class corresponding to a <value> element within an Enum 33 Metadata: Root node that also provides tree construction functionality. 34 Tag: A node corresponding to a top level <tag> element. 35 Typedef: A node corresponding to a <typedef> element under <types>. 36""" 37 38import sys 39import functools 40import itertools 41from collections import OrderedDict 42 43class Node(object): 44 """ 45 Base class for most nodes that are part of the Metadata graph. 46 47 Attributes (Read-Only): 48 parent: An edge to a parent Node. 49 name: A string describing the name, usually but not always the 'name' 50 attribute of the corresponding XML node. 51 """ 52 53 def __init__(self): 54 self._parent = None 55 self._name = None 56 57 @property 58 def parent(self): 59 return self._parent 60 61 @property 62 def name(self): 63 return self._name 64 65 def find_all(self, pred): 66 """ 67 Find all descendants that match the predicate. 68 69 Args: 70 pred: a predicate function that acts as a filter for a Node 71 72 Yields: 73 A sequence of all descendants for which pred(node) is true, 74 in a pre-order visit order. 75 """ 76 if pred(self): 77 yield self 78 79 if self._get_children() is None: 80 return 81 82 for i in self._get_children(): 83 for j in i.find_all(pred): 84 yield j 85 86 def find_first(self, pred): 87 """ 88 Find the first descendant that matches the predicate. 89 90 Args: 91 pred: a predicate function that acts as a filter for a Node 92 93 Returns: 94 The first Node from find_all(pred), or None if there were no results. 95 """ 96 for i in self.find_all(pred): 97 return i 98 99 return None 100 101 def find_parent_first(self, pred): 102 """ 103 Find the first ancestor that matches the predicate. 104 105 Args: 106 pred: A predicate function that acts as a filter for a Node 107 108 Returns: 109 The first ancestor closest to the node for which pred(node) is true. 110 """ 111 for i in self.find_parents(pred): 112 return i 113 114 return None 115 116 def find_parents(self, pred): 117 """ 118 Find all ancestors that match the predicate. 119 120 Args: 121 pred: A predicate function that acts as a filter for a Node 122 123 Yields: 124 A sequence of all ancestors (closest to furthest) from the node, 125 where pred(node) is true. 126 """ 127 parent = self.parent 128 129 while parent is not None: 130 if pred(parent): 131 yield parent 132 parent = parent.parent 133 134 def sort_children(self): 135 """ 136 Sorts the immediate children in-place. 137 """ 138 self._sort_by_name(self._children) 139 140 def _sort_by_name(self, what): 141 what.sort(key=lambda x: x.name) 142 143 def _get_name(self): 144 return lambda x: x.name 145 146 # Iterate over all children nodes. None when node doesn't support children. 147 def _get_children(self): 148 return (i for i in self._children) 149 150 def _children_name_map_matching(self, match=lambda x: True): 151 d = {} 152 for i in self._get_children(): 153 if match(i): 154 d[i.name] = i 155 return d 156 157 @staticmethod 158 def _dictionary_by_name(values): 159 d = OrderedDict() 160 for i in values: 161 d[i.name] = i 162 163 return d 164 165 def validate_tree(self): 166 """ 167 Sanity check the tree recursively, ensuring for a node n, all children's 168 parents are also n. 169 170 Returns: 171 True if validation succeeds, False otherwise. 172 """ 173 succ = True 174 children = self._get_children() 175 if children is None: 176 return True 177 178 for child in self._get_children(): 179 if child.parent != self: 180 print("ERROR: Node '%s' doesn't match the parent (expected: %s, actual %s)" \ 181 % (child, self, child.parent), file=sys.stderr) 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. 199 types: An iterable of all Typedef instances available in the graph. 200 """ 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 """ 207# Private 208 self._entries = [] 209 # kind => { name => entry } 210 self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} } 211 self._entries_ordered = [] # list of ordered Entry/Clone instances 212 self._clones = [] 213 214# Public (Read Only) 215 self._name = None 216 self._parent = None 217 self._outer_namespaces = None 218 self._tags = [] 219 self._types = [] 220 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 232 @property 233 def types(self): 234 return (i for i in self._types) 235 236 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: 261 self._tags.append(Tag(tag, self, description)) 262 263 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 290 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 305 self._entries_ordered.append(e) 306 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 """ 320 # 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) 325 self._entries_ordered.append(c) 326 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 346 for ancestor in p.find_parents(lambda x: not isinstance(x, Metadata)): 347 ancestor._leafs.remove(p) 348 349 # remove from global list 350 self._clones.remove(p) 351 self._entry_map[p.kind].pop(p.name) 352 self._entries_ordered.remove(p) 353 354 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] 369 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() 384 self._construct_types() 385 self.validate_tree() 386 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 401 if p not in tag.entries: 402 tag._entries.append(p) 403 404 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 414 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 if (p.hal_major_version == 0): 420 p._hal_major_version = target_entry._hal_major_version 421 p._hal_minor_version = target_entry._hal_minor_version 422 # should not throw if we pass validation 423 # but can happen when importing obsolete CSV entries 424 if target_entry is None: 425 print("WARNING: Clone entry '%s' target kind '%s' has no corresponding entry" \ 426 % (p.name, p.target_kind), file=sys.stderr) 427 428 def _construct_outer_namespaces(self): 429 430 if self._outer_namespaces is None: #the first time this runs 431 self._outer_namespaces = [] 432 433 root = self._dictionary_by_name(self._outer_namespaces) 434 for ons_name, ons in root.items(): 435 ons._leafs = [] 436 437 for p in self._entries_ordered: 438 ons_name = p.get_outer_namespace() 439 ons = root.get(ons_name, OuterNamespace(ons_name, self)) 440 root[ons_name] = ons 441 442 if p not in ons._leafs: 443 ons._leafs.append(p) 444 445 for ons_name, ons in root.items(): 446 447 ons.validate_tree() 448 449 self._construct_sections(ons) 450 451 if ons not in self._outer_namespaces: 452 self._outer_namespaces.append(ons) 453 454 ons.validate_tree() 455 456 def _construct_sections(self, outer_namespace): 457 458 sections_dict = self._dictionary_by_name(outer_namespace.sections) 459 for sec_name, sec in sections_dict.items(): 460 sec._leafs = [] 461 sec.validate_tree() 462 463 for p in outer_namespace._leafs: 464 does_exist = sections_dict.get(p.get_section()) 465 466 sec = sections_dict.get(p.get_section(), \ 467 Section(p.get_section(), outer_namespace)) 468 sections_dict[p.get_section()] = sec 469 470 sec.validate_tree() 471 472 if p not in sec._leafs: 473 sec._leafs.append(p) 474 475 for sec_name, sec in sections_dict.items(): 476 477 if not sec.validate_tree(): 478 print("ERROR: Failed to validate tree in construct_sections (start), with section = '%s'" 479 % (sec), file=sys.stderr) 480 481 self._construct_kinds(sec) 482 483 if sec not in outer_namespace.sections: 484 outer_namespace._sections.append(sec) 485 486 if not sec.validate_tree(): 487 print("ERROR: Failed to validate tree in construct_sections (end), with section = '%s'" 488 % (sec), file=sys.stderr) 489 490 # 'controls', 'static' 'dynamic'. etc 491 def _construct_kinds(self, section): 492 for kind in section.kinds: 493 kind._leafs = [] 494 section.validate_tree() 495 496 group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind) 497 leaf_it = ((k, g) for k, g in group_entry_by_kind) 498 499 # allow multiple kinds with the same name. merge if adjacent 500 # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic 501 # this helps maintain ABI compatibility when adding an entry in a new kind 502 for idx, (kind_name, entry_it) in enumerate(leaf_it): 503 if idx >= len(section._kinds): 504 kind = Kind(kind_name, section) 505 section._kinds.append(kind) 506 section.validate_tree() 507 508 kind = section._kinds[idx] 509 510 for p in entry_it: 511 if p not in kind._leafs: 512 kind._leafs.append(p) 513 514 for kind in section._kinds: 515 kind.validate_tree() 516 self._construct_inner_namespaces(kind) 517 kind.validate_tree() 518 self._construct_entries(kind) 519 kind.validate_tree() 520 521 if not section.validate_tree(): 522 print("ERROR: Failed to validate tree in construct_kinds, with kind = '%s'" % (kind), 523 file=sys.stderr) 524 525 if not kind.validate_tree(): 526 print("ERROR: Failed to validate tree in construct_kinds, with kind = '%s'" % (kind), 527 file=sys.stderr) 528 529 def _construct_inner_namespaces(self, parent, depth=0): 530 #parent is InnerNamespace or Kind 531 ins_dict = self._dictionary_by_name(parent.namespaces) 532 for name, ins in ins_dict.items(): 533 ins._leafs = [] 534 535 for p in parent._leafs: 536 ins_list = p.get_inner_namespace_list() 537 538 if len(ins_list) > depth: 539 ins_str = ins_list[depth] 540 ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent)) 541 ins_dict[ins_str] = ins 542 543 if p not in ins._leafs: 544 ins._leafs.append(p) 545 546 for name, ins in ins_dict.items(): 547 ins.validate_tree() 548 # construct children INS 549 self._construct_inner_namespaces(ins, depth + 1) 550 ins.validate_tree() 551 # construct children entries 552 self._construct_entries(ins, depth + 1) 553 554 if ins not in parent.namespaces: 555 parent._namespaces.append(ins) 556 557 if not ins.validate_tree(): 558 print("ERROR: Failed to validate tree in construct_inner_namespaces, with ins = '%s'" 559 % (ins), file=sys.stderr) 560 561 # doesnt construct the entries, so much as links them 562 def _construct_entries(self, parent, depth=0): 563 #parent is InnerNamespace or Kind 564 entry_dict = self._dictionary_by_name(parent.entries) 565 for p in parent._leafs: 566 ins_list = p.get_inner_namespace_list() 567 568 if len(ins_list) == depth: 569 entry = entry_dict.get(p.name, p) 570 entry_dict[p.name] = entry 571 572 for name, entry in entry_dict.items(): 573 574 old_parent = entry.parent 575 entry._parent = parent 576 577 if entry not in parent.entries: 578 parent._entries.append(entry) 579 580 if old_parent is not None and old_parent != parent: 581 print("ERROR: Parent changed from '%s' to '%s' for entry '%s'" 582 % (old_parent.name, parent.name, entry.name), file = sys.stderr) 583 584 def _get_children(self): 585 if self.outer_namespaces is not None: 586 for i in self.outer_namespaces: 587 yield i 588 589 if self.tags is not None: 590 for i in self.tags: 591 yield i 592 593class Tag(Node): 594 """ 595 A tag Node corresponding to a top-level <tag> element. 596 597 Attributes (Read-Only): 598 name: alias for id 599 id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC' 600 description: The description of the tag, the contents of the <tag> element. 601 parent: An edge to the parent, which is always the Metadata root node. 602 entries: A sequence of edges to entries/clones that are using this Tag. 603 """ 604 def __init__(self, name, parent, description=""): 605 self._name = name # 'id' attribute in XML 606 self._id = name 607 self._description = description 608 self._parent = parent 609 610 # all entries that have this tag, including clones 611 self._entries = [] # filled in by Metadata#construct_tags 612 613 @property 614 def id(self): 615 return self._id 616 617 @property 618 def description(self): 619 return self._description 620 621 @property 622 def entries(self): 623 return (i for i in self._entries) 624 625 def _get_children(self): 626 return None 627 628class Typedef(Node): 629 """ 630 A typedef Node corresponding to a <typedef> element under a top-level <types>. 631 632 Attributes (Read-Only): 633 name: The name of this typedef as a string. 634 languages: A dictionary of 'language name' -> 'fully qualified class'. 635 parent: An edge to the parent, which is always the Metadata root node. 636 entries: An iterable over all entries which reference this typedef. 637 """ 638 def __init__(self, name, parent, languages=None): 639 self._name = name 640 self._parent = parent 641 642 # all entries that have this typedef 643 self._entries = [] # filled in by Metadata#construct_types 644 645 self._languages = languages or {} 646 647 @property 648 def languages(self): 649 return self._languages 650 651 @property 652 def entries(self): 653 return (i for i in self._entries) 654 655 def _get_children(self): 656 return None 657 658class OuterNamespace(Node): 659 """ 660 A node corresponding to a <namespace> element under <metadata> 661 662 Attributes (Read-Only): 663 name: The name attribute of the <namespace name="foo"> element. 664 parent: An edge to the parent, which is always the Metadata root node. 665 sections: A sequence of Section children. 666 """ 667 def __init__(self, name, parent, sections=[]): 668 self._name = name 669 self._parent = parent # MetadataSet 670 self._sections = sections[:] 671 self._leafs = [] 672 673 self._children = self._sections 674 675 @property 676 def sections(self): 677 return (i for i in self._sections) 678 679class Section(Node): 680 """ 681 A node corresponding to a <section> element under <namespace> 682 683 Attributes (Read-Only): 684 name: The name attribute of the <section name="foo"> element. 685 parent: An edge to the parent, which is always an OuterNamespace instance. 686 description: A string description of the section, or None. 687 kinds: A sequence of Kind children. 688 merged_kinds: A sequence of virtual Kind children, 689 with each Kind's children merged by the kind.name 690 hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have 691 """ 692 def __init__(self, name, parent, description=None, kinds=[]): 693 self._name = name 694 self._parent = parent 695 self._description = description 696 self._kinds = kinds[:] 697 698 self._leafs = [] 699 700 @property 701 def description(self): 702 return self._description 703 704 @property 705 def kinds(self): 706 return (i for i in self._kinds) 707 708 @property 709 def hal_versions(self): 710 hal_versions = set() 711 for i in self._kinds: 712 for entry in i.entries: 713 hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) ) 714 for namespace in i.namespaces: 715 hal_versions.update(namespace.hal_versions) 716 return hal_versions 717 718 def sort_children(self): 719 self.validate_tree() 720 # order is always controls,static,dynamic 721 find_child = lambda x: [i for i in self._get_children() if i.name == x] 722 new_lst = find_child('controls') \ 723 + find_child('static') \ 724 + find_child('dynamic') 725 self._kinds = new_lst 726 self.validate_tree() 727 728 def _get_children(self): 729 return (i for i in self.kinds) 730 731 @property 732 def merged_kinds(self): 733 734 def aggregate_by_name(acc, el): 735 existing = [i for i in acc if i.name == el.name] 736 if existing: 737 k = existing[0] 738 else: 739 k = Kind(el.name, el.parent) 740 acc.append(k) 741 742 k._namespaces.extend(el._namespaces) 743 k._entries.extend(el._entries) 744 745 return acc 746 747 new_kinds_lst = functools.reduce(aggregate_by_name, self.kinds, []) 748 749 for k in new_kinds_lst: 750 yield k 751 752 def combine_kinds_into_single_node(self): 753 r""" 754 Combines the section's Kinds into a single node. 755 756 Combines all the children (kinds) of this section into a single 757 virtual Kind node. 758 759 Returns: 760 A new Kind node that collapses all Kind siblings into one, combining 761 all their children together. 762 763 For example, given self.kinds == [ x, y ] 764 765 x y z 766 / | | \ --> / | | \ 767 a b c d a b c d 768 769 a new instance z is returned in this example. 770 771 Remarks: 772 The children of the kinds are the same references as before, that is 773 their parents will point to the old parents and not to the new parent. 774 """ 775 combined = Kind(name="combined", parent=self) 776 777 for k in self._get_children(): 778 combined._namespaces.extend(k.namespaces) 779 combined._entries.extend(k.entries) 780 781 return combined 782 783class Kind(Node): 784 """ 785 A node corresponding to one of: <static>,<dynamic>,<controls> under a 786 <section> element. 787 788 Attributes (Read-Only): 789 name: A string which is one of 'static', 'dynamic, or 'controls'. 790 parent: An edge to the parent, which is always a Section instance. 791 namespaces: A sequence of InnerNamespace children. 792 entries: A sequence of Entry/Clone children. 793 merged_entries: A sequence of MergedEntry virtual nodes from entries 794 """ 795 def __init__(self, name, parent): 796 self._name = name 797 self._parent = parent 798 self._namespaces = [] 799 self._entries = [] 800 801 self._leafs = [] 802 803 @property 804 def namespaces(self): 805 return self._namespaces 806 807 @property 808 def entries(self): 809 return self._entries 810 811 @property 812 def merged_entries(self): 813 for i in self.entries: 814 yield i.merge() 815 816 def sort_children(self): 817 self._namespaces.sort(key=self._get_name()) 818 self._entries.sort(key=self._get_name()) 819 820 def _get_children(self): 821 for i in self.namespaces: 822 yield i 823 for i in self.entries: 824 yield i 825 826 def combine_children_by_name(self): 827 r""" 828 Combine multiple children with the same name into a single node. 829 830 Returns: 831 A new Kind where all of the children with the same name were combined. 832 833 For example: 834 835 Given a Kind k: 836 837 k 838 / | \ 839 a b c 840 | | | 841 d e f 842 843 a.name == "foo" 844 b.name == "foo" 845 c.name == "bar" 846 847 The returned Kind will look like this: 848 849 k' 850 / \ 851 a' c' 852 / | | 853 d e f 854 855 Remarks: 856 This operation is not recursive. To combine the grandchildren and other 857 ancestors, call this method on the ancestor nodes. 858 """ 859 return Kind._combine_children_by_name(self, new_type=type(self)) 860 861 # new_type is either Kind or InnerNamespace 862 @staticmethod 863 def _combine_children_by_name(self, new_type): 864 new_ins_dict = OrderedDict() 865 new_ent_dict = OrderedDict() 866 867 for ins in self.namespaces: 868 new_ins = new_ins_dict.setdefault(ins.name, 869 InnerNamespace(ins.name, parent=self)) 870 new_ins._namespaces.extend(ins.namespaces) 871 new_ins._entries.extend(ins.entries) 872 873 for ent in self.entries: 874 new_ent = new_ent_dict.setdefault(ent.name, 875 ent.merge()) 876 877 kind = new_type(self.name, self.parent) 878 kind._namespaces = new_ins_dict.values() 879 kind._entries = new_ent_dict.values() 880 881 return kind 882 883class InnerNamespace(Node): 884 """ 885 A node corresponding to a <namespace> which is an ancestor of a Kind. 886 These namespaces may have other namespaces recursively, or entries as leafs. 887 888 Attributes (Read-Only): 889 name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo' 890 parent: An edge to the parent, which is an InnerNamespace or a Kind. 891 namespaces: A sequence of InnerNamespace children. 892 entries: A sequence of Entry/Clone children. 893 merged_entries: A sequence of MergedEntry virtual nodes from entries 894 hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have 895 """ 896 def __init__(self, name, parent): 897 self._name = name 898 self._parent = parent 899 self._namespaces = [] 900 self._entries = [] 901 self._leafs = [] 902 903 @property 904 def namespaces(self): 905 return self._namespaces 906 907 @property 908 def entries(self): 909 return self._entries 910 911 @property 912 def hal_versions(self): 913 hal_versions = set() 914 for entry in self.entries: 915 hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) ) 916 for namespace in self.namespaces: 917 hal_versions.update(namespace.hal_versions) 918 return hal_versions 919 920 @property 921 def merged_entries(self): 922 for i in self.entries: 923 yield i.merge() 924 925 def sort_children(self): 926 self._namespaces.sort(key=self._get_name()) 927 self._entries.sort(key=self._get_name()) 928 929 def _get_children(self): 930 for i in self.namespaces: 931 yield i 932 for i in self.entries: 933 yield i 934 935 def combine_children_by_name(self): 936 r""" 937 Combine multiple children with the same name into a single node. 938 939 Returns: 940 A new InnerNamespace where all of the children with the same name were 941 combined. 942 943 For example: 944 945 Given an InnerNamespace i: 946 947 i 948 / | \ 949 a b c 950 | | | 951 d e f 952 953 a.name == "foo" 954 b.name == "foo" 955 c.name == "bar" 956 957 The returned InnerNamespace will look like this: 958 959 i' 960 / \ 961 a' c' 962 / | | 963 d e f 964 965 Remarks: 966 This operation is not recursive. To combine the grandchildren and other 967 ancestors, call this method on the ancestor nodes. 968 """ 969 return Kind._combine_children_by_name(self, new_type=type(self)) 970 971class EnumValue(Node): 972 """ 973 A class corresponding to a <value> element within an <enum> within an <entry>. 974 975 Attributes (Read-Only): 976 name: A string, e.g. 'ON' or 'OFF' 977 id: An optional numeric string, e.g. '0' or '0xFF' 978 deprecated: A boolean, True if the enum should be deprecated. 979 optional: A boolean 980 visibility: A string, one of "system", "java_public", "ndk_public", "hidden", "public", 981 "fwk_java_public", "fwk_public", "fwk_ndk_public", "extension", "fwk_system_public" 982 notes: A string describing the notes, or None. 983 sdk_notes: A string describing extra notes for public SDK only 984 ndk_notes: A string describing extra notes for public NDK only 985 parent: An edge to the parent, always an Enum instance. 986 hal_major_version: The major HIDL HAL version this value was first added in 987 hal_minor_version: The minor HIDL HAL version this value was first added in 988 aconfig_flag: The aconfig flag name that determines if this enum value is actually enabled 989 """ 990 def __init__(self, name, parent, 991 id=None, deprecated=False, optional=False, visibility=None, notes=None, 992 sdk_notes=None, ndk_notes=None, hal_version='3.2', aconfig_flag=None): 993 self._name = name # str, e.g. 'ON' or 'OFF' 994 self._id = id # int, e.g. '0' 995 self._deprecated = deprecated # bool 996 self._optional = optional # bool 997 self._visibility = visibility # None or str; None is same as public 998 self._notes = notes # None or str 999 self._sdk_notes = sdk_notes # None or str 1000 self._ndk_notes = ndk_notes # None or str 1001 self._parent = parent 1002 if hal_version is None: 1003 if parent is not None and parent.parent is not None: 1004 self._hal_major_version = parent.parent.hal_major_version 1005 self._hal_minor_version = parent.parent.hal_minor_version 1006 else: 1007 self._hal_major_version = 3 1008 self._hal_minor_version = 2 1009 else: 1010 self._hal_major_version = int(hal_version.partition('.')[0]) 1011 self._hal_minor_version = int(hal_version.partition('.')[2]) 1012 1013 self._aconfig_flag = aconfig_flag 1014 if self._aconfig_flag is None: 1015 if parent is not None and parent.parent is not None: 1016 self._aconfig_flag = parent.parent.aconfig_flag 1017 1018 @property 1019 def id(self): 1020 return self._id 1021 1022 @property 1023 def deprecated(self): 1024 return self._deprecated 1025 1026 @property 1027 def optional(self): 1028 return self._optional 1029 1030 @property 1031 def visibility(self): 1032 return self._visibility 1033 1034 @property 1035 def applied_visibility(self): 1036 return self._visibility or 'public' 1037 1038 @property 1039 def hidl_comment_string(self): 1040 parent_enum = None 1041 if (self.parent is not None and self.parent.parent is not None): 1042 parent_enum = self.parent.parent 1043 if parent_enum is not None and parent_enum.visibility in ('fwk_only', 'fwk_java_public',\ 1044 'fwk_public', 'fwk_ndk_public') or self._visibility in ('fwk_only', 'fwk_java_public',\ 1045 'fwk_public', 'fwk_ndk_public'): 1046 return ',' 1047 return ', // HIDL v' + str(self._hal_major_version) + '.' + str(self.hal_minor_version) 1048 1049 @property 1050 def hidden(self): 1051 return self.visibility in {'hidden', 'ndk_public', 'test', 'extension', 'fwk_system_public'} 1052 1053 @property 1054 def ndk_hidden(self): 1055 return self._visibility in {'hidden', 'java_public', 'test'} 1056 1057 @property 1058 def notes(self): 1059 return self._notes 1060 1061 @property 1062 def sdk_notes(self): 1063 return self._sdk_notes 1064 1065 @property 1066 def ndk_notes(self): 1067 return self._ndk_notes 1068 1069 @property 1070 def hal_major_version(self): 1071 return self._hal_major_version 1072 1073 @property 1074 def hal_minor_version(self): 1075 return self._hal_minor_version 1076 1077 @property 1078 def aconfig_flag(self): 1079 return self._aconfig_flag 1080 1081 def _get_children(self): 1082 return None 1083 1084class Enum(Node): 1085 """ 1086 A class corresponding to an <enum> element within an <entry>. 1087 1088 Attributes (Read-Only): 1089 parent: An edge to the parent, always an Entry instance. 1090 values: A sequence of EnumValue children. 1091 has_values_with_id: A boolean representing if any of the children have a 1092 non-empty id property. 1093 """ 1094 def __init__(self, parent, values, ids={}, deprecateds=[], 1095 optionals=[], visibilities={}, notes={}, sdk_notes={}, ndk_notes={}, 1096 hal_versions={}, aconfig_flags={}): 1097 self._parent = parent 1098 self._name = None 1099 self._values = \ 1100 [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, visibilities.get(val), \ 1101 notes.get(val), sdk_notes.get(val), ndk_notes.get(val), hal_versions.get(val), \ 1102 aconfig_flags.get(val)) \ 1103 for val in values ] 1104 1105 @property 1106 def values(self): 1107 return (i for i in self._values) 1108 1109 @property 1110 def has_values_with_id(self): 1111 return bool(any(i for i in self.values if i.id)) 1112 1113 def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version): 1114 return bool(any(i for i in self.values if i.hal_major_version == hal_major_version and i.hal_minor_version == hal_minor_version)) 1115 1116 def _get_children(self): 1117 return (i for i in self._values) 1118 1119class Entry(Node): 1120 """ 1121 A node corresponding to an <entry> element. 1122 1123 Attributes (Read-Only): 1124 parent: An edge to the parent node, which is an InnerNamespace or Kind. 1125 name: The fully qualified name string, e.g. 'android.shading.mode' 1126 name_short: The name attribute from <entry name="mode">, e.g. mode 1127 type: The type attribute from <entry type="bar"> 1128 kind: A string ('static', 'dynamic', 'controls') corresponding to the 1129 ancestor Kind#name 1130 container: The container attribute from <entry container="array">, or None. 1131 container_sizes: A sequence of size strings or None if container is None. 1132 enum: An Enum instance if the enum attribute is true, None otherwise. 1133 visibility: The visibility of this entry ('system', 'hidden', 'public' etc) 1134 across the system. System entries are only visible in native code 1135 headers. Hidden entries are marked @hide in managed code, while 1136 public entries are visible in the Android SDK. 1137 applied_visibility: As visibility, but always valid, defaulting to 'system' 1138 if no visibility is given for an entry. 1139 applied_ndk_visible: Always valid. Default is 'false'. 1140 Set to 'true' when the visibility implied entry is visible 1141 in NDK. 1142 synthetic: The C-level visibility of this entry ('false', 'true'). 1143 Synthetic entries will not be generated into the native metadata 1144 list of entries (in C code). In general a synthetic entry is 1145 glued together at the Java layer from multiple visibiltity=hidden 1146 entries. 1147 hwlevel: The lowest hardware level at which the entry is guaranteed 1148 to be supported by the camera device. All devices with higher 1149 hwlevels will also include this entry. None means that the 1150 entry is optional on any hardware level. 1151 permission_needed: Flags whether the tag needs extra camera permission. 1152 aconfig_flag: The aconfig flag name that determines if the entry is actually enabled 1153 deprecated: Marks an entry as @Deprecated in the Java layer; if within an 1154 unreleased version this needs to be removed altogether. If applied 1155 to an entry from an older release, then this means the entry 1156 should be ignored by newer code. 1157 optional: a bool representing the optional attribute, which denotes the entry 1158 is required for hardware level full devices, but optional for other 1159 hardware levels. None if not present. 1160 applied_optional: As optional but always valid, defaulting to False if no 1161 optional attribute is present. 1162 tuple_values: A sequence of strings describing the tuple values, 1163 None if container is not 'tuple'. 1164 description: A string description, or None. 1165 deprecation_description: A string describing the reason for deprecation. Must be present 1166 if deprecated is true, otherwise may be None. 1167 range: A string range, or None. 1168 units: A string units, or None. 1169 tags: A sequence of Tag nodes associated with this Entry. 1170 type_notes: A string describing notes for the type, or None. 1171 typedef: A Typedef associated with this Entry, or None. 1172 1173 Remarks: 1174 Subclass Clone can be used interchangeable with an Entry, 1175 for when we don't care about the underlying type. 1176 1177 parent and tags edges are invalid until after Metadata#construct_graph 1178 has been invoked. 1179 """ 1180 def __init__(self, **kwargs): 1181 """ 1182 Instantiate a new Entry node. 1183 1184 Args: 1185 name: A string with the fully qualified name, e.g. 'android.shading.mode' 1186 type: A string describing the type, e.g. 'int32' 1187 kind: A string describing the kind, e.g. 'static' 1188 hal_version: A string for the initial HIDL HAL metadata version this entry 1189 was added in 1190 1191 Args (if container): 1192 container: A string describing the container, e.g. 'array' or 'tuple' 1193 container_sizes: A list of string sizes if a container, or None otherwise 1194 1195 Args (if container is 'tuple'): 1196 tuple_values: A list of tuple values, e.g. ['width', 'height'] 1197 1198 Args (if the 'enum' attribute is true): 1199 enum: A boolean, True if this is an enum, False otherwise 1200 enum_values: A list of value strings, e.g. ['ON', 'OFF'] 1201 enum_optionals: A list of optional enum values, e.g. ['OFF'] 1202 enum_notes: A dictionary of value->notes strings. 1203 enum_ids: A dictionary of value->id strings. 1204 enum_hal_versions: A dictionary of value->hal version strings 1205 enum_aconfig_flags: A dictionary of value->aconfig flag name strings 1206 1207 Args (if the 'deprecated' attribute is true): 1208 deprecation_description: A string explaining the deprecation, to be added 1209 to the Java-layer @deprecated tag 1210 1211 Args (optional): 1212 description: A string with a description of the entry. 1213 range: A string with the range of the values of the entry, e.g. '>= 0' 1214 units: A string with the units of the values, e.g. 'inches' 1215 details: A string with the detailed documentation for the entry 1216 hal_details: A string with the HAL implementation details for the entry 1217 ndk_details: A string with the extra NDK API documentation for the entry= 1218 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1'] 1219 type_notes: A string with the notes for the type 1220 visibility: A string describing the visibility, eg 'system', 'hidden', 1221 'public' 1222 synthetic: A bool to mark whether this entry is visible only at the Java 1223 layer (True), or at both layers (False = default). 1224 hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full') 1225 deprecated: A bool to mark whether this is @Deprecated at the Java layer 1226 (default = False). 1227 optional: A bool to mark whether optional for non-full hardware devices 1228 typedef: A string corresponding to a typedef's name attribute. 1229 """ 1230 1231 if kwargs.get('type') is None: 1232 print("ERROR: Missing type for entry '%s' kind '%s'" 1233 % (kwargs.get('name'), kwargs.get('kind')), file=sys.stderr) 1234 1235 # Attributes are Read-Only, but edges may be mutated by 1236 # Metadata, particularly during construct_graph 1237 1238 self._name = kwargs['name'] 1239 self._type = kwargs['type'] 1240 self._kind = kwargs['kind'] # static, dynamic, or controls 1241 1242 self._init_common(**kwargs) 1243 1244 @property 1245 def type(self): 1246 return self._type 1247 1248 @property 1249 def kind(self): 1250 return self._kind 1251 1252 @property 1253 def hal_major_version(self): 1254 return self._hal_major_version 1255 1256 @property 1257 def hal_minor_version(self): 1258 return self._hal_minor_version 1259 1260 @property 1261 def visibility(self): 1262 return self._visibility 1263 1264 @property 1265 def applied_visibility(self): 1266 return self._visibility or 'system' 1267 1268 @property 1269 def hidl_comment_string(self): 1270 if self._visibility in ('fwk_only', 'fwk_java_public', 'fwk_public', 'fwk_ndk_public'): 1271 return self._visibility 1272 visibility_lj = str(self.applied_visibility).ljust(12) 1273 return visibility_lj + ' | HIDL v' + str(self._hal_major_version) + '.' + str(self._hal_minor_version) 1274 1275 @property 1276 def applied_ndk_visible(self): 1277 if self._visibility in ("public", "ndk_public", "fwk_public", "fwk_ndk_public"): 1278 return "true" 1279 return "false" 1280 1281 @property 1282 def synthetic(self): 1283 return self._synthetic 1284 1285 @property 1286 def hwlevel(self): 1287 return self._hwlevel 1288 1289 @property 1290 def deprecated(self): 1291 return self._deprecated 1292 1293 @property 1294 def deprecation_description(self): 1295 return self._deprecation_description 1296 1297 @property 1298 def permission_needed(self): 1299 return self._permission_needed or "false" 1300 1301 @property 1302 def aconfig_flag(self): 1303 return self._aconfig_flag 1304 1305 # TODO: optional should just return hwlevel is None 1306 @property 1307 def optional(self): 1308 return self._optional 1309 1310 @property 1311 def applied_optional(self): 1312 return self._optional or False 1313 1314 @property 1315 def name_short(self): 1316 return self.get_name_minimal() 1317 1318 @property 1319 def container(self): 1320 return self._container 1321 1322 @property 1323 def container_sizes(self): 1324 if self._container_sizes is None: 1325 return None 1326 else: 1327 return (i for i in self._container_sizes) 1328 1329 @property 1330 def tuple_values(self): 1331 if self._tuple_values is None: 1332 return None 1333 else: 1334 return (i for i in self._tuple_values) 1335 1336 @property 1337 def description(self): 1338 return self._description 1339 1340 @property 1341 def range(self): 1342 return self._range 1343 1344 @property 1345 def units(self): 1346 return self._units 1347 1348 @property 1349 def details(self): 1350 return self._details 1351 1352 @property 1353 def hal_details(self): 1354 return self._hal_details 1355 1356 @property 1357 def ndk_details(self): 1358 return self._ndk_details 1359 1360 @property 1361 def applied_ndk_details(self): 1362 return (self._details or "") + (self._ndk_details or "") 1363 1364 @property 1365 def tags(self): 1366 if self._tags is None: 1367 return None 1368 else: 1369 return (i for i in self._tags) 1370 1371 @property 1372 def type_notes(self): 1373 return self._type_notes 1374 1375 @property 1376 def typedef(self): 1377 return self._typedef 1378 1379 @property 1380 def enum(self): 1381 return self._enum 1382 1383 @property 1384 def session_characteristics_key_since(self): 1385 return self._session_characteristics_key_since 1386 1387 def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version): 1388 if self._enum is not None: 1389 return self._enum.has_new_values_added_in_hal_version(hal_major_version,hal_minor_version) 1390 else: 1391 return False 1392 1393 def _get_children(self): 1394 if self.enum: 1395 yield self.enum 1396 1397 def sort_children(self): 1398 return None 1399 1400 def is_clone(self): 1401 """ 1402 Whether or not this is a Clone instance. 1403 1404 Returns: 1405 False 1406 """ 1407 return False 1408 1409 def _init_common(self, **kwargs): 1410 1411 self._parent = None # filled in by Metadata::_construct_entries 1412 1413 self._container = kwargs.get('container') 1414 self._container_sizes = kwargs.get('container_sizes') 1415 1416 hal_version = kwargs.get('hal_version') 1417 if hal_version is None: 1418 if self.is_clone(): 1419 self._hal_major_version = 0 1420 self._hal_minor_version = 0 1421 else: 1422 self._hal_major_version = 3 1423 self._hal_minor_version = 2 1424 else: 1425 self._hal_major_version = int(hal_version.partition('.')[0]) 1426 self._hal_minor_version = int(hal_version.partition('.')[2]) 1427 1428 self._aconfig_flag = kwargs.get('aconfig_flag') 1429 1430 # access these via the 'enum' prop 1431 enum_values = kwargs.get('enum_values') 1432 enum_deprecateds = kwargs.get('enum_deprecateds') 1433 enum_optionals = kwargs.get('enum_optionals') 1434 enum_visibilities = kwargs.get('enum_visibilities') 1435 enum_notes = kwargs.get('enum_notes') # { value => notes } 1436 enum_sdk_notes = kwargs.get('enum_sdk_notes') # { value => sdk_notes } 1437 enum_ndk_notes = kwargs.get('enum_ndk_notes') # { value => ndk_notes } 1438 enum_ids = kwargs.get('enum_ids') # { value => notes } 1439 enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions } 1440 enum_aconfig_flags = kwargs.get('enum_aconfig_flags') # { value => aconfig flags } 1441 1442 self._tuple_values = kwargs.get('tuple_values') 1443 1444 self._description = kwargs.get('description') 1445 self._range = kwargs.get('range') 1446 self._units = kwargs.get('units') 1447 self._details = kwargs.get('details') 1448 self._hal_details = kwargs.get('hal_details') 1449 self._ndk_details = kwargs.get('ndk_details') 1450 1451 self._tag_ids = kwargs.get('tag_ids', []) 1452 self._tags = None # Filled in by Metadata::_construct_tags 1453 1454 self._type_notes = kwargs.get('type_notes') 1455 self._type_name = kwargs.get('type_name') 1456 self._typedef = None # Filled in by Metadata::_construct_types 1457 1458 if kwargs.get('enum', False): 1459 self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals, 1460 enum_visibilities, enum_notes, enum_sdk_notes, enum_ndk_notes, 1461 enum_hal_versions, enum_aconfig_flags) 1462 else: 1463 self._enum = None 1464 1465 self._visibility = kwargs.get('visibility') 1466 self._synthetic = kwargs.get('synthetic', False) 1467 self._hwlevel = kwargs.get('hwlevel') 1468 self._deprecated = kwargs.get('deprecated', False) 1469 self._deprecation_description = kwargs.get('deprecation_description') 1470 1471 self._permission_needed = kwargs.get('permission_needed') 1472 self._optional = kwargs.get('optional') 1473 self._ndk_visible = kwargs.get('ndk_visible') 1474 1475 self._session_characteristics_key_since = None \ 1476 if not kwargs.get('session_characteristics_key_since') \ 1477 else int(kwargs.get('session_characteristics_key_since')) 1478 1479 self._property_keys = kwargs 1480 1481 def merge(self): 1482 """ 1483 Copy the attributes into a new entry, merging it with the target entry 1484 if it's a clone. 1485 """ 1486 return MergedEntry(self) 1487 1488 # Helpers for accessing less than the fully qualified name 1489 1490 def get_name_as_list(self): 1491 """ 1492 Returns the name as a list split by a period. 1493 1494 For example: 1495 entry.name is 'android.lens.info.shading' 1496 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading'] 1497 """ 1498 return self.name.split(".") 1499 1500 def get_inner_namespace_list(self): 1501 """ 1502 Returns the inner namespace part of the name as a list 1503 1504 For example: 1505 entry.name is 'android.lens.info.shading' 1506 entry.get_inner_namespace_list() == ['info'] 1507 """ 1508 return self.get_name_as_list()[2:-1] 1509 1510 def get_outer_namespace(self): 1511 """ 1512 Returns the outer namespace as a string. 1513 1514 For example: 1515 entry.name is 'android.lens.info.shading' 1516 entry.get_outer_namespace() == 'android' 1517 1518 Remarks: 1519 Since outer namespaces are non-recursive, 1520 and each entry has one, this does not need to be a list. 1521 """ 1522 return self.get_name_as_list()[0] 1523 1524 def get_section(self): 1525 """ 1526 Returns the section as a string. 1527 1528 For example: 1529 entry.name is 'android.lens.info.shading' 1530 entry.get_section() == '' 1531 1532 Remarks: 1533 Since outer namespaces are non-recursive, 1534 and each entry has one, this does not need to be a list. 1535 """ 1536 return self.get_name_as_list()[1] 1537 1538 def get_name_minimal(self): 1539 """ 1540 Returns only the last component of the fully qualified name as a string. 1541 1542 For example: 1543 entry.name is 'android.lens.info.shading' 1544 entry.get_name_minimal() == 'shading' 1545 1546 Remarks: 1547 entry.name_short it an alias for this 1548 """ 1549 return self.get_name_as_list()[-1] 1550 1551 def get_path_without_name(self): 1552 """ 1553 Returns a string path to the entry, with the name component excluded. 1554 1555 For example: 1556 entry.name is 'android.lens.info.shading' 1557 entry.get_path_without_name() == 'android.lens.info' 1558 """ 1559 return ".".join(self.get_name_as_list()[0:-1]) 1560 1561 1562class Clone(Entry): 1563 """ 1564 A Node corresponding to a <clone> element. It has all the attributes of an 1565 <entry> element (Entry) plus the additions specified below. 1566 1567 Attributes (Read-Only): 1568 entry: an edge to an Entry object that this targets 1569 target_kind: A string describing the kind of the target entry. 1570 name: a string of the name, same as entry.name 1571 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic' 1572 for the <clone> element. 1573 type: always None, since a clone cannot override the type. 1574 """ 1575 def __init__(self, entry=None, **kwargs): 1576 """ 1577 Instantiate a new Clone node. 1578 1579 Args: 1580 name: A string with the fully qualified name, e.g. 'android.shading.mode' 1581 type: A string describing the type, e.g. 'int32' 1582 kind: A string describing the kind, e.g. 'static' 1583 target_kind: A string for the kind of the target entry, e.g. 'dynamic' 1584 hal_version: A string for the initial HIDL HAL metadata version this entry 1585 was added in 1586 1587 Args (if container): 1588 container: A string describing the container, e.g. 'array' or 'tuple' 1589 container_sizes: A list of string sizes if a container, or None otherwise 1590 1591 Args (if container is 'tuple'): 1592 tuple_values: A list of tuple values, e.g. ['width', 'height'] 1593 1594 Args (if the 'enum' attribute is true): 1595 enum: A boolean, True if this is an enum, False otherwise 1596 enum_values: A list of value strings, e.g. ['ON', 'OFF'] 1597 enum_optionals: A list of optional enum values, e.g. ['OFF'] 1598 enum_notes: A dictionary of value->notes strings. 1599 enum_ids: A dictionary of value->id strings. 1600 1601 Args (optional): 1602 entry: An edge to the corresponding target Entry. 1603 description: A string with a description of the entry. 1604 range: A string with the range of the values of the entry, e.g. '>= 0' 1605 units: A string with the units of the values, e.g. 'inches' 1606 details: A string with the detailed documentation for the entry 1607 hal_details: A string with the HAL implementation details for the entry 1608 ndk_details: A string with the extra NDK documentation for the entry 1609 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1'] 1610 type_notes: A string with the notes for the type 1611 1612 Remarks: 1613 Note that type is not specified since it has to be the same as the 1614 entry.type. 1615 """ 1616 self._entry = entry # Entry object 1617 self._target_kind = kwargs['target_kind'] 1618 self._name = kwargs['name'] # same as entry.name 1619 self._kind = kwargs['kind'] 1620 1621 # illegal to override the type, it should be the same as the entry 1622 self._type = None 1623 # the rest of the kwargs are optional 1624 # can be used to override the regular entry data 1625 self._init_common(**kwargs) 1626 1627 @property 1628 def entry(self): 1629 return self._entry 1630 1631 @property 1632 def target_kind(self): 1633 return self._target_kind 1634 1635 def is_clone(self): 1636 """ 1637 Whether or not this is a Clone instance. 1638 1639 Returns: 1640 True 1641 """ 1642 return True 1643 1644class MergedEntry(Entry): 1645 """ 1646 A MergedEntry has all the attributes of a Clone and its target Entry merged 1647 together. 1648 1649 Remarks: 1650 Useful when we want to 'unfold' a clone into a real entry by copying out 1651 the target entry data. In this case we don't care about distinguishing 1652 a clone vs an entry. 1653 """ 1654 def __init__(self, entry): 1655 """ 1656 Create a new instance of MergedEntry. 1657 1658 Args: 1659 entry: An Entry or Clone instance 1660 """ 1661 props_distinct = ['description', 'units', 'range', 'details', 1662 'hal_details', 'ndk_details', 'tags', 'kind', 1663 'deprecation_description'] 1664 1665 for p in props_distinct: 1666 p = '_' + p 1667 if entry.is_clone(): 1668 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p)) 1669 else: 1670 setattr(self, p, getattr(entry, p)) 1671 1672 props_common = ['parent', 'name', 'container', 1673 'container_sizes', 'enum', 1674 'tuple_values', 1675 'type', 1676 'type_notes', 1677 'visibility', 1678 'ndk_visible', 1679 'synthetic', 1680 'hwlevel', 1681 'deprecated', 1682 'optional', 1683 'typedef', 1684 'hal_major_version', 1685 'hal_minor_version', 1686 'permission_needed', 1687 'aconfig_flag', 1688 'session_characteristics_key_since' 1689 ] 1690 1691 for p in props_common: 1692 p = '_' + p 1693 if entry.is_clone(): 1694 setattr(self, p, getattr(entry.entry, p)) 1695 else: 1696 setattr(self, p, getattr(entry, p)) 1697