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