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", "extension" 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 or self._visibility in ('fwk_only', 'fwk_java_public'): 1045 return ',' 1046 return ', // HIDL v' + str(self._hal_major_version) + '.' + str(self.hal_minor_version) 1047 1048 @property 1049 def hidden(self): 1050 return self.visibility in {'hidden', 'ndk_public', 'test', 'extension'} 1051 1052 @property 1053 def ndk_hidden(self): 1054 return self._visibility in {'hidden', 'java_public', 'test'} 1055 1056 @property 1057 def notes(self): 1058 return self._notes 1059 1060 @property 1061 def sdk_notes(self): 1062 return self._sdk_notes 1063 1064 @property 1065 def ndk_notes(self): 1066 return self._ndk_notes 1067 1068 @property 1069 def hal_major_version(self): 1070 return self._hal_major_version 1071 1072 @property 1073 def hal_minor_version(self): 1074 return self._hal_minor_version 1075 1076 @property 1077 def aconfig_flag(self): 1078 return self._aconfig_flag 1079 1080 def _get_children(self): 1081 return None 1082 1083class Enum(Node): 1084 """ 1085 A class corresponding to an <enum> element within an <entry>. 1086 1087 Attributes (Read-Only): 1088 parent: An edge to the parent, always an Entry instance. 1089 values: A sequence of EnumValue children. 1090 has_values_with_id: A boolean representing if any of the children have a 1091 non-empty id property. 1092 """ 1093 def __init__(self, parent, values, ids={}, deprecateds=[], 1094 optionals=[], visibilities={}, notes={}, sdk_notes={}, ndk_notes={}, 1095 hal_versions={}, aconfig_flags={}): 1096 self._parent = parent 1097 self._name = None 1098 self._values = \ 1099 [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, visibilities.get(val), \ 1100 notes.get(val), sdk_notes.get(val), ndk_notes.get(val), hal_versions.get(val), \ 1101 aconfig_flags.get(val)) \ 1102 for val in values ] 1103 1104 @property 1105 def values(self): 1106 return (i for i in self._values) 1107 1108 @property 1109 def has_values_with_id(self): 1110 return bool(any(i for i in self.values if i.id)) 1111 1112 def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version): 1113 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)) 1114 1115 def _get_children(self): 1116 return (i for i in self._values) 1117 1118class Entry(Node): 1119 """ 1120 A node corresponding to an <entry> element. 1121 1122 Attributes (Read-Only): 1123 parent: An edge to the parent node, which is an InnerNamespace or Kind. 1124 name: The fully qualified name string, e.g. 'android.shading.mode' 1125 name_short: The name attribute from <entry name="mode">, e.g. mode 1126 type: The type attribute from <entry type="bar"> 1127 kind: A string ('static', 'dynamic', 'controls') corresponding to the 1128 ancestor Kind#name 1129 container: The container attribute from <entry container="array">, or None. 1130 container_sizes: A sequence of size strings or None if container is None. 1131 enum: An Enum instance if the enum attribute is true, None otherwise. 1132 visibility: The visibility of this entry ('system', 'hidden', 'public') 1133 across the system. System entries are only visible in native code 1134 headers. Hidden entries are marked @hide in managed code, while 1135 public entries are visible in the Android SDK. 1136 applied_visibility: As visibility, but always valid, defaulting to 'system' 1137 if no visibility is given for an entry. 1138 applied_ndk_visible: Always valid. Default is 'false'. 1139 Set to 'true' when the visibility implied entry is visible 1140 in NDK. 1141 synthetic: The C-level visibility of this entry ('false', 'true'). 1142 Synthetic entries will not be generated into the native metadata 1143 list of entries (in C code). In general a synthetic entry is 1144 glued together at the Java layer from multiple visibiltity=hidden 1145 entries. 1146 hwlevel: The lowest hardware level at which the entry is guaranteed 1147 to be supported by the camera device. All devices with higher 1148 hwlevels will also include this entry. None means that the 1149 entry is optional on any hardware level. 1150 permission_needed: Flags whether the tag needs extra camera permission. 1151 aconfig_flag: The aconfig flag name that determines if the entry is actually enabled 1152 deprecated: Marks an entry as @Deprecated in the Java layer; if within an 1153 unreleased version this needs to be removed altogether. If applied 1154 to an entry from an older release, then this means the entry 1155 should be ignored by newer code. 1156 optional: a bool representing the optional attribute, which denotes the entry 1157 is required for hardware level full devices, but optional for other 1158 hardware levels. None if not present. 1159 applied_optional: As optional but always valid, defaulting to False if no 1160 optional attribute is present. 1161 tuple_values: A sequence of strings describing the tuple values, 1162 None if container is not 'tuple'. 1163 description: A string description, or None. 1164 deprecation_description: A string describing the reason for deprecation. Must be present 1165 if deprecated is true, otherwise may be None. 1166 range: A string range, or None. 1167 units: A string units, or None. 1168 tags: A sequence of Tag nodes associated with this Entry. 1169 type_notes: A string describing notes for the type, or None. 1170 typedef: A Typedef associated with this Entry, or None. 1171 1172 Remarks: 1173 Subclass Clone can be used interchangeable with an Entry, 1174 for when we don't care about the underlying type. 1175 1176 parent and tags edges are invalid until after Metadata#construct_graph 1177 has been invoked. 1178 """ 1179 def __init__(self, **kwargs): 1180 """ 1181 Instantiate a new Entry node. 1182 1183 Args: 1184 name: A string with the fully qualified name, e.g. 'android.shading.mode' 1185 type: A string describing the type, e.g. 'int32' 1186 kind: A string describing the kind, e.g. 'static' 1187 hal_version: A string for the initial HIDL HAL metadata version this entry 1188 was added in 1189 1190 Args (if container): 1191 container: A string describing the container, e.g. 'array' or 'tuple' 1192 container_sizes: A list of string sizes if a container, or None otherwise 1193 1194 Args (if container is 'tuple'): 1195 tuple_values: A list of tuple values, e.g. ['width', 'height'] 1196 1197 Args (if the 'enum' attribute is true): 1198 enum: A boolean, True if this is an enum, False otherwise 1199 enum_values: A list of value strings, e.g. ['ON', 'OFF'] 1200 enum_optionals: A list of optional enum values, e.g. ['OFF'] 1201 enum_notes: A dictionary of value->notes strings. 1202 enum_ids: A dictionary of value->id strings. 1203 enum_hal_versions: A dictionary of value->hal version strings 1204 enum_aconfig_flags: A dictionary of value->aconfig flag name strings 1205 1206 Args (if the 'deprecated' attribute is true): 1207 deprecation_description: A string explaining the deprecation, to be added 1208 to the Java-layer @deprecated tag 1209 1210 Args (optional): 1211 description: A string with a description of the entry. 1212 range: A string with the range of the values of the entry, e.g. '>= 0' 1213 units: A string with the units of the values, e.g. 'inches' 1214 details: A string with the detailed documentation for the entry 1215 hal_details: A string with the HAL implementation details for the entry 1216 ndk_details: A string with the extra NDK API documentation for the entry= 1217 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1'] 1218 type_notes: A string with the notes for the type 1219 visibility: A string describing the visibility, eg 'system', 'hidden', 1220 'public' 1221 synthetic: A bool to mark whether this entry is visible only at the Java 1222 layer (True), or at both layers (False = default). 1223 hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full') 1224 deprecated: A bool to mark whether this is @Deprecated at the Java layer 1225 (default = False). 1226 optional: A bool to mark whether optional for non-full hardware devices 1227 typedef: A string corresponding to a typedef's name attribute. 1228 """ 1229 1230 if kwargs.get('type') is None: 1231 print("ERROR: Missing type for entry '%s' kind '%s'" 1232 % (kwargs.get('name'), kwargs.get('kind')), file=sys.stderr) 1233 1234 # Attributes are Read-Only, but edges may be mutated by 1235 # Metadata, particularly during construct_graph 1236 1237 self._name = kwargs['name'] 1238 self._type = kwargs['type'] 1239 self._kind = kwargs['kind'] # static, dynamic, or controls 1240 1241 self._init_common(**kwargs) 1242 1243 @property 1244 def type(self): 1245 return self._type 1246 1247 @property 1248 def kind(self): 1249 return self._kind 1250 1251 @property 1252 def hal_major_version(self): 1253 return self._hal_major_version 1254 1255 @property 1256 def hal_minor_version(self): 1257 return self._hal_minor_version 1258 1259 @property 1260 def visibility(self): 1261 return self._visibility 1262 1263 @property 1264 def applied_visibility(self): 1265 return self._visibility or 'system' 1266 1267 @property 1268 def hidl_comment_string(self): 1269 if self._visibility in ('fwk_only', 'fwk_java_public'): 1270 return self._visibility 1271 visibility_lj = str(self.applied_visibility).ljust(12) 1272 return visibility_lj + ' | HIDL v' + str(self._hal_major_version) + '.' + str(self._hal_minor_version) 1273 1274 @property 1275 def applied_ndk_visible(self): 1276 if self._visibility in ("public", "ndk_public"): 1277 return "true" 1278 return "false" 1279 1280 @property 1281 def synthetic(self): 1282 return self._synthetic 1283 1284 @property 1285 def hwlevel(self): 1286 return self._hwlevel 1287 1288 @property 1289 def deprecated(self): 1290 return self._deprecated 1291 1292 @property 1293 def deprecation_description(self): 1294 return self._deprecation_description 1295 1296 @property 1297 def permission_needed(self): 1298 return self._permission_needed or "false" 1299 1300 @property 1301 def aconfig_flag(self): 1302 return self._aconfig_flag 1303 1304 # TODO: optional should just return hwlevel is None 1305 @property 1306 def optional(self): 1307 return self._optional 1308 1309 @property 1310 def applied_optional(self): 1311 return self._optional or False 1312 1313 @property 1314 def name_short(self): 1315 return self.get_name_minimal() 1316 1317 @property 1318 def container(self): 1319 return self._container 1320 1321 @property 1322 def container_sizes(self): 1323 if self._container_sizes is None: 1324 return None 1325 else: 1326 return (i for i in self._container_sizes) 1327 1328 @property 1329 def tuple_values(self): 1330 if self._tuple_values is None: 1331 return None 1332 else: 1333 return (i for i in self._tuple_values) 1334 1335 @property 1336 def description(self): 1337 return self._description 1338 1339 @property 1340 def range(self): 1341 return self._range 1342 1343 @property 1344 def units(self): 1345 return self._units 1346 1347 @property 1348 def details(self): 1349 return self._details 1350 1351 @property 1352 def hal_details(self): 1353 return self._hal_details 1354 1355 @property 1356 def ndk_details(self): 1357 return self._ndk_details 1358 1359 @property 1360 def applied_ndk_details(self): 1361 return (self._details or "") + (self._ndk_details or "") 1362 1363 @property 1364 def tags(self): 1365 if self._tags is None: 1366 return None 1367 else: 1368 return (i for i in self._tags) 1369 1370 @property 1371 def type_notes(self): 1372 return self._type_notes 1373 1374 @property 1375 def typedef(self): 1376 return self._typedef 1377 1378 @property 1379 def enum(self): 1380 return self._enum 1381 1382 @property 1383 def session_characteristics_key_since(self): 1384 return self._session_characteristics_key_since 1385 1386 def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version): 1387 if self._enum is not None: 1388 return self._enum.has_new_values_added_in_hal_version(hal_major_version,hal_minor_version) 1389 else: 1390 return False 1391 1392 def _get_children(self): 1393 if self.enum: 1394 yield self.enum 1395 1396 def sort_children(self): 1397 return None 1398 1399 def is_clone(self): 1400 """ 1401 Whether or not this is a Clone instance. 1402 1403 Returns: 1404 False 1405 """ 1406 return False 1407 1408 def _init_common(self, **kwargs): 1409 1410 self._parent = None # filled in by Metadata::_construct_entries 1411 1412 self._container = kwargs.get('container') 1413 self._container_sizes = kwargs.get('container_sizes') 1414 1415 hal_version = kwargs.get('hal_version') 1416 if hal_version is None: 1417 if self.is_clone(): 1418 self._hal_major_version = 0 1419 self._hal_minor_version = 0 1420 else: 1421 self._hal_major_version = 3 1422 self._hal_minor_version = 2 1423 else: 1424 self._hal_major_version = int(hal_version.partition('.')[0]) 1425 self._hal_minor_version = int(hal_version.partition('.')[2]) 1426 1427 self._aconfig_flag = kwargs.get('aconfig_flag') 1428 1429 # access these via the 'enum' prop 1430 enum_values = kwargs.get('enum_values') 1431 enum_deprecateds = kwargs.get('enum_deprecateds') 1432 enum_optionals = kwargs.get('enum_optionals') 1433 enum_visibilities = kwargs.get('enum_visibilities') 1434 enum_notes = kwargs.get('enum_notes') # { value => notes } 1435 enum_sdk_notes = kwargs.get('enum_sdk_notes') # { value => sdk_notes } 1436 enum_ndk_notes = kwargs.get('enum_ndk_notes') # { value => ndk_notes } 1437 enum_ids = kwargs.get('enum_ids') # { value => notes } 1438 enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions } 1439 enum_aconfig_flags = kwargs.get('enum_aconfig_flags') # { value => aconfig flags } 1440 1441 self._tuple_values = kwargs.get('tuple_values') 1442 1443 self._description = kwargs.get('description') 1444 self._range = kwargs.get('range') 1445 self._units = kwargs.get('units') 1446 self._details = kwargs.get('details') 1447 self._hal_details = kwargs.get('hal_details') 1448 self._ndk_details = kwargs.get('ndk_details') 1449 1450 self._tag_ids = kwargs.get('tag_ids', []) 1451 self._tags = None # Filled in by Metadata::_construct_tags 1452 1453 self._type_notes = kwargs.get('type_notes') 1454 self._type_name = kwargs.get('type_name') 1455 self._typedef = None # Filled in by Metadata::_construct_types 1456 1457 if kwargs.get('enum', False): 1458 self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals, 1459 enum_visibilities, enum_notes, enum_sdk_notes, enum_ndk_notes, 1460 enum_hal_versions, enum_aconfig_flags) 1461 else: 1462 self._enum = None 1463 1464 self._visibility = kwargs.get('visibility') 1465 self._synthetic = kwargs.get('synthetic', False) 1466 self._hwlevel = kwargs.get('hwlevel') 1467 self._deprecated = kwargs.get('deprecated', False) 1468 self._deprecation_description = kwargs.get('deprecation_description') 1469 1470 self._permission_needed = kwargs.get('permission_needed') 1471 self._optional = kwargs.get('optional') 1472 self._ndk_visible = kwargs.get('ndk_visible') 1473 1474 self._session_characteristics_key_since = None \ 1475 if not kwargs.get('session_characteristics_key_since') \ 1476 else int(kwargs.get('session_characteristics_key_since')) 1477 1478 self._property_keys = kwargs 1479 1480 def merge(self): 1481 """ 1482 Copy the attributes into a new entry, merging it with the target entry 1483 if it's a clone. 1484 """ 1485 return MergedEntry(self) 1486 1487 # Helpers for accessing less than the fully qualified name 1488 1489 def get_name_as_list(self): 1490 """ 1491 Returns the name as a list split by a period. 1492 1493 For example: 1494 entry.name is 'android.lens.info.shading' 1495 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading'] 1496 """ 1497 return self.name.split(".") 1498 1499 def get_inner_namespace_list(self): 1500 """ 1501 Returns the inner namespace part of the name as a list 1502 1503 For example: 1504 entry.name is 'android.lens.info.shading' 1505 entry.get_inner_namespace_list() == ['info'] 1506 """ 1507 return self.get_name_as_list()[2:-1] 1508 1509 def get_outer_namespace(self): 1510 """ 1511 Returns the outer namespace as a string. 1512 1513 For example: 1514 entry.name is 'android.lens.info.shading' 1515 entry.get_outer_namespace() == 'android' 1516 1517 Remarks: 1518 Since outer namespaces are non-recursive, 1519 and each entry has one, this does not need to be a list. 1520 """ 1521 return self.get_name_as_list()[0] 1522 1523 def get_section(self): 1524 """ 1525 Returns the section as a string. 1526 1527 For example: 1528 entry.name is 'android.lens.info.shading' 1529 entry.get_section() == '' 1530 1531 Remarks: 1532 Since outer namespaces are non-recursive, 1533 and each entry has one, this does not need to be a list. 1534 """ 1535 return self.get_name_as_list()[1] 1536 1537 def get_name_minimal(self): 1538 """ 1539 Returns only the last component of the fully qualified name as a string. 1540 1541 For example: 1542 entry.name is 'android.lens.info.shading' 1543 entry.get_name_minimal() == 'shading' 1544 1545 Remarks: 1546 entry.name_short it an alias for this 1547 """ 1548 return self.get_name_as_list()[-1] 1549 1550 def get_path_without_name(self): 1551 """ 1552 Returns a string path to the entry, with the name component excluded. 1553 1554 For example: 1555 entry.name is 'android.lens.info.shading' 1556 entry.get_path_without_name() == 'android.lens.info' 1557 """ 1558 return ".".join(self.get_name_as_list()[0:-1]) 1559 1560 1561class Clone(Entry): 1562 """ 1563 A Node corresponding to a <clone> element. It has all the attributes of an 1564 <entry> element (Entry) plus the additions specified below. 1565 1566 Attributes (Read-Only): 1567 entry: an edge to an Entry object that this targets 1568 target_kind: A string describing the kind of the target entry. 1569 name: a string of the name, same as entry.name 1570 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic' 1571 for the <clone> element. 1572 type: always None, since a clone cannot override the type. 1573 """ 1574 def __init__(self, entry=None, **kwargs): 1575 """ 1576 Instantiate a new Clone node. 1577 1578 Args: 1579 name: A string with the fully qualified name, e.g. 'android.shading.mode' 1580 type: A string describing the type, e.g. 'int32' 1581 kind: A string describing the kind, e.g. 'static' 1582 target_kind: A string for the kind of the target entry, e.g. 'dynamic' 1583 hal_version: A string for the initial HIDL HAL metadata version this entry 1584 was added in 1585 1586 Args (if container): 1587 container: A string describing the container, e.g. 'array' or 'tuple' 1588 container_sizes: A list of string sizes if a container, or None otherwise 1589 1590 Args (if container is 'tuple'): 1591 tuple_values: A list of tuple values, e.g. ['width', 'height'] 1592 1593 Args (if the 'enum' attribute is true): 1594 enum: A boolean, True if this is an enum, False otherwise 1595 enum_values: A list of value strings, e.g. ['ON', 'OFF'] 1596 enum_optionals: A list of optional enum values, e.g. ['OFF'] 1597 enum_notes: A dictionary of value->notes strings. 1598 enum_ids: A dictionary of value->id strings. 1599 1600 Args (optional): 1601 entry: An edge to the corresponding target Entry. 1602 description: A string with a description of the entry. 1603 range: A string with the range of the values of the entry, e.g. '>= 0' 1604 units: A string with the units of the values, e.g. 'inches' 1605 details: A string with the detailed documentation for the entry 1606 hal_details: A string with the HAL implementation details for the entry 1607 ndk_details: A string with the extra NDK documentation for the entry 1608 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1'] 1609 type_notes: A string with the notes for the type 1610 1611 Remarks: 1612 Note that type is not specified since it has to be the same as the 1613 entry.type. 1614 """ 1615 self._entry = entry # Entry object 1616 self._target_kind = kwargs['target_kind'] 1617 self._name = kwargs['name'] # same as entry.name 1618 self._kind = kwargs['kind'] 1619 1620 # illegal to override the type, it should be the same as the entry 1621 self._type = None 1622 # the rest of the kwargs are optional 1623 # can be used to override the regular entry data 1624 self._init_common(**kwargs) 1625 1626 @property 1627 def entry(self): 1628 return self._entry 1629 1630 @property 1631 def target_kind(self): 1632 return self._target_kind 1633 1634 def is_clone(self): 1635 """ 1636 Whether or not this is a Clone instance. 1637 1638 Returns: 1639 True 1640 """ 1641 return True 1642 1643class MergedEntry(Entry): 1644 """ 1645 A MergedEntry has all the attributes of a Clone and its target Entry merged 1646 together. 1647 1648 Remarks: 1649 Useful when we want to 'unfold' a clone into a real entry by copying out 1650 the target entry data. In this case we don't care about distinguishing 1651 a clone vs an entry. 1652 """ 1653 def __init__(self, entry): 1654 """ 1655 Create a new instance of MergedEntry. 1656 1657 Args: 1658 entry: An Entry or Clone instance 1659 """ 1660 props_distinct = ['description', 'units', 'range', 'details', 1661 'hal_details', 'ndk_details', 'tags', 'kind', 1662 'deprecation_description'] 1663 1664 for p in props_distinct: 1665 p = '_' + p 1666 if entry.is_clone(): 1667 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p)) 1668 else: 1669 setattr(self, p, getattr(entry, p)) 1670 1671 props_common = ['parent', 'name', 'container', 1672 'container_sizes', 'enum', 1673 'tuple_values', 1674 'type', 1675 'type_notes', 1676 'visibility', 1677 'ndk_visible', 1678 'synthetic', 1679 'hwlevel', 1680 'deprecated', 1681 'optional', 1682 'typedef', 1683 'hal_major_version', 1684 'hal_minor_version', 1685 'permission_needed', 1686 'aconfig_flag', 1687 'session_characteristics_key_since' 1688 ] 1689 1690 for p in props_common: 1691 p = '_' + p 1692 if entry.is_clone(): 1693 setattr(self, p, getattr(entry.entry, p)) 1694 else: 1695 setattr(self, p, getattr(entry, p)) 1696