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