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