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