1#!/usr/bin/python 2 3# 4# Copyright (C) 2012 The Android Open Source Project 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19""" 20A set of classes (models) each closely representing an XML node in the 21metadata_definitions.xml file. 22 23 Node: Base class for most nodes. 24 Entry: A node corresponding to <entry> elements. 25 Clone: A node corresponding to <clone> elements. 26 MergedEntry: A node corresponding to either <entry> or <clone> elements. 27 Kind: A node corresponding to <dynamic>, <static>, <controls> elements. 28 InnerNamespace: A node corresponding to a <namespace> nested under a <kind>. 29 OuterNamespace: A node corresponding to a <namespace> with <kind> children. 30 Section: A node corresponding to a <section> element. 31 Enum: A class corresponding an <enum> element within an <entry> 32 EnumValue: A class corresponding to a <value> element within an Enum 33 Metadata: Root node that also provides tree construction functionality. 34 Tag: A node corresponding to a top level <tag> element. 35 Typedef: A node corresponding to a <typedef> element under <types>. 36""" 37 38import sys 39import functools 40import itertools 41from collections import OrderedDict 42 43class Node(object): 44 """ 45 Base class for most nodes that are part of the Metadata graph. 46 47 Attributes (Read-Only): 48 parent: An edge to a parent Node. 49 name: A string describing the name, usually but not always the 'name' 50 attribute of the corresponding XML node. 51 """ 52 53 def __init__(self): 54 self._parent = None 55 self._name = None 56 57 @property 58 def parent(self): 59 return self._parent 60 61 @property 62 def name(self): 63 return self._name 64 65 def find_all(self, pred): 66 """ 67 Find all descendants that match the predicate. 68 69 Args: 70 pred: a predicate function that acts as a filter for a Node 71 72 Yields: 73 A sequence of all descendants for which pred(node) is true, 74 in a pre-order visit order. 75 """ 76 if pred(self): 77 yield self 78 79 if self._get_children() is None: 80 return 81 82 for i in self._get_children(): 83 for j in i.find_all(pred): 84 yield j 85 86 def find_first(self, pred): 87 """ 88 Find the first descendant that matches the predicate. 89 90 Args: 91 pred: a predicate function that acts as a filter for a Node 92 93 Returns: 94 The first Node from find_all(pred), or None if there were no results. 95 """ 96 for i in self.find_all(pred): 97 return i 98 99 return None 100 101 def find_parent_first(self, pred): 102 """ 103 Find the first ancestor that matches the predicate. 104 105 Args: 106 pred: A predicate function that acts as a filter for a Node 107 108 Returns: 109 The first ancestor closest to the node for which pred(node) is true. 110 """ 111 for i in self.find_parents(pred): 112 return i 113 114 return None 115 116 def find_parents(self, pred): 117 """ 118 Find all ancestors that match the predicate. 119 120 Args: 121 pred: A predicate function that acts as a filter for a Node 122 123 Yields: 124 A sequence of all ancestors (closest to furthest) from the node, 125 where pred(node) is true. 126 """ 127 parent = self.parent 128 129 while parent is not None: 130 if pred(parent): 131 yield parent 132 parent = parent.parent 133 134 def sort_children(self): 135 """ 136 Sorts the immediate children in-place. 137 """ 138 self._sort_by_name(self._children) 139 140 def _sort_by_name(self, what): 141 what.sort(key=lambda x: x.name) 142 143 def _get_name(self): 144 return lambda x: x.name 145 146 # Iterate over all children nodes. None when node doesn't support children. 147 def _get_children(self): 148 return (i for i in self._children) 149 150 def _children_name_map_matching(self, match=lambda x: True): 151 d = {} 152 for i in self._get_children(): 153 if match(i): 154 d[i.name] = i 155 return d 156 157 @staticmethod 158 def _dictionary_by_name(values): 159 d = OrderedDict() 160 for i in values: 161 d[i.name] = i 162 163 return d 164 165 def validate_tree(self): 166 """ 167 Sanity check the tree recursively, ensuring for a node n, all children's 168 parents are also n. 169 170 Returns: 171 True if validation succeeds, False otherwise. 172 """ 173 succ = True 174 children = self._get_children() 175 if children is None: 176 return True 177 178 for child in self._get_children(): 179 if child.parent != self: 180 print("ERROR: Node '%s' doesn't match the parent (expected: %s, actual %s)" \ 181 % (child, self, child.parent), file=sys.stderr) 182 succ = False 183 184 succ = child.validate_tree() and succ 185 186 return succ 187 188 def __str__(self): 189 return "<%s name='%s'>" %(self.__class__, self.name) 190 191class Metadata(Node): 192 """ 193 A node corresponding to a <metadata> entry. 194 195 Attributes (Read-Only): 196 parent: An edge to the parent Node. This is always None for Metadata. 197 outer_namespaces: A sequence of immediate OuterNamespace children. 198 tags: A sequence of all Tag instances available in the graph. 199 types: An iterable of all Typedef instances available in the graph. 200 """ 201 202 def __init__(self): 203 """ 204 Initialize with no children. Use insert_* functions and then 205 construct_graph() to build up the Metadata from some source. 206 """ 207# Private 208 self._entries = [] 209 # kind => { name => entry } 210 self._entry_map = { 'static': {}, 'dynamic': {}, 'controls': {} } 211 self._entries_ordered = [] # list of ordered Entry/Clone instances 212 self._clones = [] 213 214# Public (Read Only) 215 self._name = None 216 self._parent = None 217 self._outer_namespaces = None 218 self._tags = [] 219 self._types = [] 220 221 @property 222 def outer_namespaces(self): 223 if self._outer_namespaces is None: 224 return None 225 else: 226 return (i for i in self._outer_namespaces) 227 228 @property 229 def tags(self): 230 return (i for i in self._tags) 231 232 @property 233 def types(self): 234 return (i for i in self._types) 235 236 def _get_properties(self): 237 238 for i in self._entries: 239 yield i 240 241 for i in self._clones: 242 yield i 243 244 def insert_tag(self, tag, description=""): 245 """ 246 Insert a tag into the metadata. 247 248 Args: 249 tag: A string identifier for a tag. 250 description: A string description for a tag. 251 252 Example: 253 metadata.insert_tag("BC", "Backwards Compatibility for old API") 254 255 Remarks: 256 Subsequent calls to insert_tag with the same tag are safe (they will 257 be ignored). 258 """ 259 tag_ids = [tg.name for tg in self.tags if tg.name == tag] 260 if not tag_ids: 261 self._tags.append(Tag(tag, self, description)) 262 263 def insert_type(self, type_name, type_selector="typedef", **kwargs): 264 """ 265 Insert a type into the metadata. 266 267 Args: 268 type_name: A type's name 269 type_selector: The selector for the type, e.g. 'typedef' 270 271 Args (if type_selector == 'typedef'): 272 languages: A map of 'language name' -> 'fully qualified class path' 273 274 Example: 275 metadata.insert_type('rectangle', 'typedef', 276 { 'java': 'android.graphics.Rect' }) 277 278 Remarks: 279 Subsequent calls to insert_type with the same type name are safe (they 280 will be ignored) 281 """ 282 283 if type_selector != 'typedef': 284 raise ValueError("Unsupported type_selector given " + type_selector) 285 286 type_names = [tp.name for tp in self.types if tp.name == tp] 287 if not type_names: 288 self._types.append(Typedef(type_name, self, kwargs.get('languages'))) 289 290 def insert_entry(self, entry): 291 """ 292 Insert an entry into the metadata. 293 294 Args: 295 entry: A key-value dictionary describing an entry. Refer to 296 Entry#__init__ for the keys required/optional. 297 298 Remarks: 299 Subsequent calls to insert_entry with the same entry+kind name are safe 300 (they will be ignored). 301 """ 302 e = Entry(**entry) 303 self._entries.append(e) 304 self._entry_map[e.kind][e.name] = e 305 self._entries_ordered.append(e) 306 307 def insert_clone(self, clone): 308 """ 309 Insert a clone into the metadata. 310 311 Args: 312 clone: A key-value dictionary describing a clone. Refer to 313 Clone#__init__ for the keys required/optional. 314 315 Remarks: 316 Subsequent calls to insert_clone with the same clone+kind name are safe 317 (they will be ignored). Also the target entry need not be inserted 318 ahead of the clone entry. 319 """ 320 # figure out corresponding entry later. allow clone insert, entry insert 321 entry = None 322 c = Clone(entry, **clone) 323 self._entry_map[c.kind][c.name] = c 324 self._clones.append(c) 325 self._entries_ordered.append(c) 326 327 def prune_clones(self): 328 """ 329 Remove all clones that don't point to an existing entry. 330 331 Remarks: 332 This should be called after all insert_entry/insert_clone calls have 333 finished. 334 """ 335 remove_list = [] 336 for p in self._clones: 337 if p.entry is None: 338 remove_list.append(p) 339 340 for p in remove_list: 341 342 # remove from parent's entries list 343 if p.parent is not None: 344 p.parent._entries.remove(p) 345 # remove from parents' _leafs list 346 for ancestor in p.find_parents(lambda x: not isinstance(x, Metadata)): 347 ancestor._leafs.remove(p) 348 349 # remove from global list 350 self._clones.remove(p) 351 self._entry_map[p.kind].pop(p.name) 352 self._entries_ordered.remove(p) 353 354 def is_entry_this_kind(self, entry, kind): 355 """ 356 Check if input entry if of input kind 357 358 Args: 359 entry: an Entry object 360 kind: a string. Possible values are "static", "dynamic", "controls" 361 362 Returns: 363 A boolean indicating whether input entry is of input kind. 364 """ 365 if kind not in ("static", "dynamic", "controls"): 366 assert(False), "Unknown kind value " + kind 367 368 return entry.name in self._entry_map[kind] 369 370 # After all entries/clones are inserted, 371 # invoke this to generate the parent/child node graph all these objects 372 def construct_graph(self): 373 """ 374 Generate the graph recursively, after which all Entry nodes will be 375 accessible recursively by crawling through the outer_namespaces sequence. 376 377 Remarks: 378 This is safe to be called multiple times at any time. It should be done at 379 least once or there will be no graph. 380 """ 381 self.validate_tree() 382 self._construct_tags() 383 self.validate_tree() 384 self._construct_types() 385 self.validate_tree() 386 self._construct_clones() 387 self.validate_tree() 388 self._construct_outer_namespaces() 389 self.validate_tree() 390 391 def _construct_tags(self): 392 tag_dict = self._dictionary_by_name(self.tags) 393 for p in self._get_properties(): 394 p._tags = [] 395 for tag_id in p._tag_ids: 396 tag = tag_dict.get(tag_id) 397 398 if tag not in p._tags: 399 p._tags.append(tag) 400 401 if p not in tag.entries: 402 tag._entries.append(p) 403 404 def _construct_types(self): 405 type_dict = self._dictionary_by_name(self.types) 406 for p in self._get_properties(): 407 if p._type_name: 408 type_node = type_dict.get(p._type_name) 409 p._typedef = type_node 410 411 if p not in type_node.entries: 412 type_node._entries.append(p) 413 414 def _construct_clones(self): 415 for p in self._clones: 416 target_kind = p.target_kind 417 target_entry = self._entry_map[target_kind].get(p.name) 418 p._entry = target_entry 419 if (p.hal_major_version == 0): 420 p._hal_major_version = target_entry._hal_major_version 421 p._hal_minor_version = target_entry._hal_minor_version 422 # should not throw if we pass validation 423 # but can happen when importing obsolete CSV entries 424 if target_entry is None: 425 print("WARNING: Clone entry '%s' target kind '%s' has no corresponding entry" \ 426 % (p.name, p.target_kind), file=sys.stderr) 427 428 def _construct_outer_namespaces(self): 429 430 if self._outer_namespaces is None: #the first time this runs 431 self._outer_namespaces = [] 432 433 root = self._dictionary_by_name(self._outer_namespaces) 434 for ons_name, ons in root.items(): 435 ons._leafs = [] 436 437 for p in self._entries_ordered: 438 ons_name = p.get_outer_namespace() 439 ons = root.get(ons_name, OuterNamespace(ons_name, self)) 440 root[ons_name] = ons 441 442 if p not in ons._leafs: 443 ons._leafs.append(p) 444 445 for ons_name, ons in root.items(): 446 447 ons.validate_tree() 448 449 self._construct_sections(ons) 450 451 if ons not in self._outer_namespaces: 452 self._outer_namespaces.append(ons) 453 454 ons.validate_tree() 455 456 def _construct_sections(self, outer_namespace): 457 458 sections_dict = self._dictionary_by_name(outer_namespace.sections) 459 for sec_name, sec in sections_dict.items(): 460 sec._leafs = [] 461 sec.validate_tree() 462 463 for p in outer_namespace._leafs: 464 does_exist = sections_dict.get(p.get_section()) 465 466 sec = sections_dict.get(p.get_section(), \ 467 Section(p.get_section(), outer_namespace)) 468 sections_dict[p.get_section()] = sec 469 470 sec.validate_tree() 471 472 if p not in sec._leafs: 473 sec._leafs.append(p) 474 475 for sec_name, sec in sections_dict.items(): 476 477 if not sec.validate_tree(): 478 print("ERROR: Failed to validate tree in construct_sections (start), with section = '%s'" 479 % (sec), file=sys.stderr) 480 481 self._construct_kinds(sec) 482 483 if sec not in outer_namespace.sections: 484 outer_namespace._sections.append(sec) 485 486 if not sec.validate_tree(): 487 print("ERROR: Failed to validate tree in construct_sections (end), with section = '%s'" 488 % (sec), file=sys.stderr) 489 490 # 'controls', 'static' 'dynamic'. etc 491 def _construct_kinds(self, section): 492 for kind in section.kinds: 493 kind._leafs = [] 494 section.validate_tree() 495 496 group_entry_by_kind = itertools.groupby(section._leafs, lambda x: x.kind) 497 leaf_it = ((k, g) for k, g in group_entry_by_kind) 498 499 # allow multiple kinds with the same name. merge if adjacent 500 # e.g. dynamic,dynamic,static,static,dynamic -> dynamic,static,dynamic 501 # this helps maintain ABI compatibility when adding an entry in a new kind 502 for idx, (kind_name, entry_it) in enumerate(leaf_it): 503 if idx >= len(section._kinds): 504 kind = Kind(kind_name, section) 505 section._kinds.append(kind) 506 section.validate_tree() 507 508 kind = section._kinds[idx] 509 510 for p in entry_it: 511 if p not in kind._leafs: 512 kind._leafs.append(p) 513 514 for kind in section._kinds: 515 kind.validate_tree() 516 self._construct_inner_namespaces(kind) 517 kind.validate_tree() 518 self._construct_entries(kind) 519 kind.validate_tree() 520 521 if not section.validate_tree(): 522 print("ERROR: Failed to validate tree in construct_kinds, with kind = '%s'" % (kind), 523 file=sys.stderr) 524 525 if not kind.validate_tree(): 526 print("ERROR: Failed to validate tree in construct_kinds, with kind = '%s'" % (kind), 527 file=sys.stderr) 528 529 def _construct_inner_namespaces(self, parent, depth=0): 530 #parent is InnerNamespace or Kind 531 ins_dict = self._dictionary_by_name(parent.namespaces) 532 for name, ins in ins_dict.items(): 533 ins._leafs = [] 534 535 for p in parent._leafs: 536 ins_list = p.get_inner_namespace_list() 537 538 if len(ins_list) > depth: 539 ins_str = ins_list[depth] 540 ins = ins_dict.get(ins_str, InnerNamespace(ins_str, parent)) 541 ins_dict[ins_str] = ins 542 543 if p not in ins._leafs: 544 ins._leafs.append(p) 545 546 for name, ins in ins_dict.items(): 547 ins.validate_tree() 548 # construct children INS 549 self._construct_inner_namespaces(ins, depth + 1) 550 ins.validate_tree() 551 # construct children entries 552 self._construct_entries(ins, depth + 1) 553 554 if ins not in parent.namespaces: 555 parent._namespaces.append(ins) 556 557 if not ins.validate_tree(): 558 print("ERROR: Failed to validate tree in construct_inner_namespaces, with ins = '%s'" 559 % (ins), file=sys.stderr) 560 561 # doesnt construct the entries, so much as links them 562 def _construct_entries(self, parent, depth=0): 563 #parent is InnerNamespace or Kind 564 entry_dict = self._dictionary_by_name(parent.entries) 565 for p in parent._leafs: 566 ins_list = p.get_inner_namespace_list() 567 568 if len(ins_list) == depth: 569 entry = entry_dict.get(p.name, p) 570 entry_dict[p.name] = entry 571 572 for name, entry in entry_dict.items(): 573 574 old_parent = entry.parent 575 entry._parent = parent 576 577 if entry not in parent.entries: 578 parent._entries.append(entry) 579 580 if old_parent is not None and old_parent != parent: 581 print("ERROR: Parent changed from '%s' to '%s' for entry '%s'" 582 % (old_parent.name, parent.name, entry.name), file = sys.stderr) 583 584 def _get_children(self): 585 if self.outer_namespaces is not None: 586 for i in self.outer_namespaces: 587 yield i 588 589 if self.tags is not None: 590 for i in self.tags: 591 yield i 592 593class Tag(Node): 594 """ 595 A tag Node corresponding to a top-level <tag> element. 596 597 Attributes (Read-Only): 598 name: alias for id 599 id: The name of the tag, e.g. for <tag id="BC"/> id = 'BC' 600 description: The description of the tag, the contents of the <tag> element. 601 parent: An edge to the parent, which is always the Metadata root node. 602 entries: A sequence of edges to entries/clones that are using this Tag. 603 """ 604 def __init__(self, name, parent, description=""): 605 self._name = name # 'id' attribute in XML 606 self._id = name 607 self._description = description 608 self._parent = parent 609 610 # all entries that have this tag, including clones 611 self._entries = [] # filled in by Metadata#construct_tags 612 613 @property 614 def id(self): 615 return self._id 616 617 @property 618 def description(self): 619 return self._description 620 621 @property 622 def entries(self): 623 return (i for i in self._entries) 624 625 def _get_children(self): 626 return None 627 628class Typedef(Node): 629 """ 630 A typedef Node corresponding to a <typedef> element under a top-level <types>. 631 632 Attributes (Read-Only): 633 name: The name of this typedef as a string. 634 languages: A dictionary of 'language name' -> 'fully qualified class'. 635 parent: An edge to the parent, which is always the Metadata root node. 636 entries: An iterable over all entries which reference this typedef. 637 """ 638 def __init__(self, name, parent, languages=None): 639 self._name = name 640 self._parent = parent 641 642 # all entries that have this typedef 643 self._entries = [] # filled in by Metadata#construct_types 644 645 self._languages = languages or {} 646 647 @property 648 def languages(self): 649 return self._languages 650 651 @property 652 def entries(self): 653 return (i for i in self._entries) 654 655 def _get_children(self): 656 return None 657 658class OuterNamespace(Node): 659 """ 660 A node corresponding to a <namespace> element under <metadata> 661 662 Attributes (Read-Only): 663 name: The name attribute of the <namespace name="foo"> element. 664 parent: An edge to the parent, which is always the Metadata root node. 665 sections: A sequence of Section children. 666 """ 667 def __init__(self, name, parent, sections=[]): 668 self._name = name 669 self._parent = parent # MetadataSet 670 self._sections = sections[:] 671 self._leafs = [] 672 673 self._children = self._sections 674 675 @property 676 def sections(self): 677 return (i for i in self._sections) 678 679class Section(Node): 680 """ 681 A node corresponding to a <section> element under <namespace> 682 683 Attributes (Read-Only): 684 name: The name attribute of the <section name="foo"> element. 685 parent: An edge to the parent, which is always an OuterNamespace instance. 686 description: A string description of the section, or None. 687 kinds: A sequence of Kind children. 688 merged_kinds: A sequence of virtual Kind children, 689 with each Kind's children merged by the kind.name 690 hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have 691 """ 692 def __init__(self, name, parent, description=None, kinds=[]): 693 self._name = name 694 self._parent = parent 695 self._description = description 696 self._kinds = kinds[:] 697 698 self._leafs = [] 699 700 @property 701 def description(self): 702 return self._description 703 704 @property 705 def kinds(self): 706 return (i for i in self._kinds) 707 708 @property 709 def hal_versions(self): 710 hal_versions = set() 711 for i in self._kinds: 712 for entry in i.entries: 713 hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) ) 714 for namespace in i.namespaces: 715 hal_versions.update(namespace.hal_versions) 716 return hal_versions 717 718 def sort_children(self): 719 self.validate_tree() 720 # order is always controls,static,dynamic 721 find_child = lambda x: [i for i in self._get_children() if i.name == x] 722 new_lst = find_child('controls') \ 723 + find_child('static') \ 724 + find_child('dynamic') 725 self._kinds = new_lst 726 self.validate_tree() 727 728 def _get_children(self): 729 return (i for i in self.kinds) 730 731 @property 732 def merged_kinds(self): 733 734 def aggregate_by_name(acc, el): 735 existing = [i for i in acc if i.name == el.name] 736 if existing: 737 k = existing[0] 738 else: 739 k = Kind(el.name, el.parent) 740 acc.append(k) 741 742 k._namespaces.extend(el._namespaces) 743 k._entries.extend(el._entries) 744 745 return acc 746 747 new_kinds_lst = functools.reduce(aggregate_by_name, self.kinds, []) 748 749 for k in new_kinds_lst: 750 yield k 751 752 def combine_kinds_into_single_node(self): 753 r""" 754 Combines the section's Kinds into a single node. 755 756 Combines all the children (kinds) of this section into a single 757 virtual Kind node. 758 759 Returns: 760 A new Kind node that collapses all Kind siblings into one, combining 761 all their children together. 762 763 For example, given self.kinds == [ x, y ] 764 765 x y z 766 / | | \ --> / | | \ 767 a b c d a b c d 768 769 a new instance z is returned in this example. 770 771 Remarks: 772 The children of the kinds are the same references as before, that is 773 their parents will point to the old parents and not to the new parent. 774 """ 775 combined = Kind(name="combined", parent=self) 776 777 for k in self._get_children(): 778 combined._namespaces.extend(k.namespaces) 779 combined._entries.extend(k.entries) 780 781 return combined 782 783class Kind(Node): 784 """ 785 A node corresponding to one of: <static>,<dynamic>,<controls> under a 786 <section> element. 787 788 Attributes (Read-Only): 789 name: A string which is one of 'static', 'dynamic, or 'controls'. 790 parent: An edge to the parent, which is always a Section instance. 791 namespaces: A sequence of InnerNamespace children. 792 entries: A sequence of Entry/Clone children. 793 merged_entries: A sequence of MergedEntry virtual nodes from entries 794 """ 795 def __init__(self, name, parent): 796 self._name = name 797 self._parent = parent 798 self._namespaces = [] 799 self._entries = [] 800 801 self._leafs = [] 802 803 @property 804 def namespaces(self): 805 return self._namespaces 806 807 @property 808 def entries(self): 809 return self._entries 810 811 @property 812 def merged_entries(self): 813 for i in self.entries: 814 yield i.merge() 815 816 def sort_children(self): 817 self._namespaces.sort(key=self._get_name()) 818 self._entries.sort(key=self._get_name()) 819 820 def _get_children(self): 821 for i in self.namespaces: 822 yield i 823 for i in self.entries: 824 yield i 825 826 def combine_children_by_name(self): 827 r""" 828 Combine multiple children with the same name into a single node. 829 830 Returns: 831 A new Kind where all of the children with the same name were combined. 832 833 For example: 834 835 Given a Kind k: 836 837 k 838 / | \ 839 a b c 840 | | | 841 d e f 842 843 a.name == "foo" 844 b.name == "foo" 845 c.name == "bar" 846 847 The returned Kind will look like this: 848 849 k' 850 / \ 851 a' c' 852 / | | 853 d e f 854 855 Remarks: 856 This operation is not recursive. To combine the grandchildren and other 857 ancestors, call this method on the ancestor nodes. 858 """ 859 return Kind._combine_children_by_name(self, new_type=type(self)) 860 861 # new_type is either Kind or InnerNamespace 862 @staticmethod 863 def _combine_children_by_name(self, new_type): 864 new_ins_dict = OrderedDict() 865 new_ent_dict = OrderedDict() 866 867 for ins in self.namespaces: 868 new_ins = new_ins_dict.setdefault(ins.name, 869 InnerNamespace(ins.name, parent=self)) 870 new_ins._namespaces.extend(ins.namespaces) 871 new_ins._entries.extend(ins.entries) 872 873 for ent in self.entries: 874 new_ent = new_ent_dict.setdefault(ent.name, 875 ent.merge()) 876 877 kind = new_type(self.name, self.parent) 878 kind._namespaces = new_ins_dict.values() 879 kind._entries = new_ent_dict.values() 880 881 return kind 882 883class InnerNamespace(Node): 884 """ 885 A node corresponding to a <namespace> which is an ancestor of a Kind. 886 These namespaces may have other namespaces recursively, or entries as leafs. 887 888 Attributes (Read-Only): 889 name: Name attribute from the element, e.g. <namespace name="foo"> -> 'foo' 890 parent: An edge to the parent, which is an InnerNamespace or a Kind. 891 namespaces: A sequence of InnerNamespace children. 892 entries: A sequence of Entry/Clone children. 893 merged_entries: A sequence of MergedEntry virtual nodes from entries 894 hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have 895 """ 896 def __init__(self, name, parent): 897 self._name = name 898 self._parent = parent 899 self._namespaces = [] 900 self._entries = [] 901 self._leafs = [] 902 903 @property 904 def namespaces(self): 905 return self._namespaces 906 907 @property 908 def entries(self): 909 return self._entries 910 911 @property 912 def hal_versions(self): 913 hal_versions = set() 914 for entry in self.entries: 915 hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) ) 916 for namespace in self.namespaces: 917 hal_versions.update(namespace.hal_versions) 918 return hal_versions 919 920 @property 921 def merged_entries(self): 922 for i in self.entries: 923 yield i.merge() 924 925 def sort_children(self): 926 self._namespaces.sort(key=self._get_name()) 927 self._entries.sort(key=self._get_name()) 928 929 def _get_children(self): 930 for i in self.namespaces: 931 yield i 932 for i in self.entries: 933 yield i 934 935 def combine_children_by_name(self): 936 r""" 937 Combine multiple children with the same name into a single node. 938 939 Returns: 940 A new InnerNamespace where all of the children with the same name were 941 combined. 942 943 For example: 944 945 Given an InnerNamespace i: 946 947 i 948 / | \ 949 a b c 950 | | | 951 d e f 952 953 a.name == "foo" 954 b.name == "foo" 955 c.name == "bar" 956 957 The returned InnerNamespace will look like this: 958 959 i' 960 / \ 961 a' c' 962 / | | 963 d e f 964 965 Remarks: 966 This operation is not recursive. To combine the grandchildren and other 967 ancestors, call this method on the ancestor nodes. 968 """ 969 return Kind._combine_children_by_name(self, new_type=type(self)) 970 971class EnumValue(Node): 972 """ 973 A class corresponding to a <value> element within an <enum> within an <entry>. 974 975 Attributes (Read-Only): 976 name: A string, e.g. 'ON' or 'OFF' 977 id: An optional numeric string, e.g. '0' or '0xFF' 978 deprecated: A boolean, True if the enum should be deprecated. 979 optional: A boolean 980 hidden: A boolean, True if the enum should be hidden. 981 ndk_hidden: A boolean, True if the enum should be hidden in NDK 982 notes: A string describing the notes, or None. 983 sdk_notes: A string describing extra notes for public SDK only 984 ndk_notes: A string describing extra notes for public NDK only 985 parent: An edge to the parent, always an Enum instance. 986 hal_major_version: The major HIDL HAL version this value was first added in 987 hal_minor_version: The minor HIDL HAL version this value was first added in 988 """ 989 def __init__(self, name, parent, 990 id=None, deprecated=False, optional=False, hidden=False, notes=None, sdk_notes=None, ndk_notes=None, ndk_hidden=False, hal_version='3.2'): 991 self._name = name # str, e.g. 'ON' or 'OFF' 992 self._id = id # int, e.g. '0' 993 self._deprecated = deprecated # bool 994 self._optional = optional # bool 995 self._hidden = hidden # bool 996 self._ndk_hidden = ndk_hidden # bool 997 self._notes = notes # None or str 998 self._sdk_notes = sdk_notes # None or str 999 self._ndk_notes = ndk_notes # None or str 1000 self._parent = parent 1001 if hal_version is None: 1002 if parent is not None and parent.parent is not None: 1003 self._hal_major_version = parent.parent.hal_major_version 1004 self._hal_minor_version = parent.parent.hal_minor_version 1005 else: 1006 self._hal_major_version = 3 1007 self._hal_minor_version = 2 1008 else: 1009 self._hal_major_version = int(hal_version.partition('.')[0]) 1010 self._hal_minor_version = int(hal_version.partition('.')[2]) 1011 1012 @property 1013 def id(self): 1014 return self._id 1015 1016 @property 1017 def deprecated(self): 1018 return self._deprecated 1019 1020 @property 1021 def optional(self): 1022 return self._optional 1023 1024 @property 1025 def hidden(self): 1026 return self._hidden 1027 1028 @property 1029 def ndk_hidden(self): 1030 return self._ndk_hidden 1031 1032 @property 1033 def notes(self): 1034 return self._notes 1035 1036 @property 1037 def sdk_notes(self): 1038 return self._sdk_notes 1039 1040 @property 1041 def ndk_notes(self): 1042 return self._ndk_notes 1043 1044 @property 1045 def hal_major_version(self): 1046 return self._hal_major_version 1047 1048 @property 1049 def hal_minor_version(self): 1050 return self._hal_minor_version 1051 1052 def _get_children(self): 1053 return None 1054 1055class Enum(Node): 1056 """ 1057 A class corresponding to an <enum> element within an <entry>. 1058 1059 Attributes (Read-Only): 1060 parent: An edge to the parent, always an Entry instance. 1061 values: A sequence of EnumValue children. 1062 has_values_with_id: A boolean representing if any of the children have a 1063 non-empty id property. 1064 """ 1065 def __init__(self, parent, values, ids={}, deprecateds=[], 1066 optionals=[], hiddens=[], notes={}, sdk_notes={}, ndk_notes={}, ndk_hiddens=[], hal_versions={}): 1067 self._parent = parent 1068 self._name = None 1069 self._values = \ 1070 [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, val in hiddens, \ 1071 notes.get(val), sdk_notes.get(val), ndk_notes.get(val), val in ndk_hiddens, hal_versions.get(val)) \ 1072 for val in values ] 1073 1074 @property 1075 def values(self): 1076 return (i for i in self._values) 1077 1078 @property 1079 def has_values_with_id(self): 1080 return bool(any(i for i in self.values if i.id)) 1081 1082 def has_new_values_added_in_hal_version(self, hal_major_version, hal_minor_version): 1083 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)) 1084 1085 def _get_children(self): 1086 return (i for i in self._values) 1087 1088class Entry(Node): 1089 """ 1090 A node corresponding to an <entry> element. 1091 1092 Attributes (Read-Only): 1093 parent: An edge to the parent node, which is an InnerNamespace or Kind. 1094 name: The fully qualified name string, e.g. 'android.shading.mode' 1095 name_short: The name attribute from <entry name="mode">, e.g. mode 1096 type: The type attribute from <entry type="bar"> 1097 kind: A string ('static', 'dynamic', 'controls') corresponding to the 1098 ancestor Kind#name 1099 container: The container attribute from <entry container="array">, or None. 1100 container_sizes: A sequence of size strings or None if container is None. 1101 enum: An Enum instance if the enum attribute is true, None otherwise. 1102 visibility: The visibility of this entry ('system', 'hidden', 'public') 1103 across the system. System entries are only visible in native code 1104 headers. Hidden entries are marked @hide in managed code, while 1105 public entries are visible in the Android SDK. 1106 applied_visibility: As visibility, but always valid, defaulting to 'system' 1107 if no visibility is given for an entry. 1108 applied_ndk_visible: Always valid. Default is 'false'. 1109 Set to 'true' when the visibility implied entry is visible 1110 in NDK. 1111 synthetic: The C-level visibility of this entry ('false', 'true'). 1112 Synthetic entries will not be generated into the native metadata 1113 list of entries (in C code). In general a synthetic entry is 1114 glued together at the Java layer from multiple visibiltity=hidden 1115 entries. 1116 hwlevel: The lowest hardware level at which the entry is guaranteed 1117 to be supported by the camera device. All devices with higher 1118 hwlevels will also include this entry. None means that the 1119 entry is optional on any hardware level. 1120 permission_needed: Flags whether the tag needs extra camera permission. 1121 deprecated: Marks an entry as @Deprecated in the Java layer; if within an 1122 unreleased version this needs to be removed altogether. If applied 1123 to an entry from an older release, then this means the entry 1124 should be ignored by newer code. 1125 optional: a bool representing the optional attribute, which denotes the entry 1126 is required for hardware level full devices, but optional for other 1127 hardware levels. None if not present. 1128 applied_optional: As optional but always valid, defaulting to False if no 1129 optional attribute is present. 1130 tuple_values: A sequence of strings describing the tuple values, 1131 None if container is not 'tuple'. 1132 description: A string description, or None. 1133 deprecation_description: A string describing the reason for deprecation. Must be present 1134 if deprecated is true, otherwise may be None. 1135 range: A string range, or None. 1136 units: A string units, or None. 1137 tags: A sequence of Tag nodes associated with this Entry. 1138 type_notes: A string describing notes for the type, or None. 1139 typedef: A Typedef associated with this Entry, or None. 1140 1141 Remarks: 1142 Subclass Clone can be used interchangeable with an Entry, 1143 for when we don't care about the underlying type. 1144 1145 parent and tags edges are invalid until after Metadata#construct_graph 1146 has been invoked. 1147 """ 1148 def __init__(self, **kwargs): 1149 """ 1150 Instantiate a new Entry node. 1151 1152 Args: 1153 name: A string with the fully qualified name, e.g. 'android.shading.mode' 1154 type: A string describing the type, e.g. 'int32' 1155 kind: A string describing the kind, e.g. 'static' 1156 hal_version: A string for the initial HIDL HAL metadata version this entry 1157 was added in 1158 1159 Args (if container): 1160 container: A string describing the container, e.g. 'array' or 'tuple' 1161 container_sizes: A list of string sizes if a container, or None otherwise 1162 1163 Args (if container is 'tuple'): 1164 tuple_values: A list of tuple values, e.g. ['width', 'height'] 1165 1166 Args (if the 'enum' attribute is true): 1167 enum: A boolean, True if this is an enum, False otherwise 1168 enum_values: A list of value strings, e.g. ['ON', 'OFF'] 1169 enum_optionals: A list of optional enum values, e.g. ['OFF'] 1170 enum_notes: A dictionary of value->notes strings. 1171 enum_ids: A dictionary of value->id strings. 1172 enum_hal_versions: A dictionary of value->hal version strings 1173 1174 Args (if the 'deprecated' attribute is true): 1175 deprecation_description: A string explaining the deprecation, to be added 1176 to the Java-layer @deprecated tag 1177 1178 Args (optional): 1179 description: A string with a description of the entry. 1180 range: A string with the range of the values of the entry, e.g. '>= 0' 1181 units: A string with the units of the values, e.g. 'inches' 1182 details: A string with the detailed documentation for the entry 1183 hal_details: A string with the HAL implementation details for the entry 1184 ndk_details: A string with the extra NDK API documentation for the entry= 1185 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1'] 1186 type_notes: A string with the notes for the type 1187 visibility: A string describing the visibility, eg 'system', 'hidden', 1188 'public' 1189 synthetic: A bool to mark whether this entry is visible only at the Java 1190 layer (True), or at both layers (False = default). 1191 hwlevel: A string of the HW level (one of 'legacy', 'limited', 'full') 1192 deprecated: A bool to mark whether this is @Deprecated at the Java layer 1193 (default = False). 1194 optional: A bool to mark whether optional for non-full hardware devices 1195 typedef: A string corresponding to a typedef's name attribute. 1196 """ 1197 1198 if kwargs.get('type') is None: 1199 print("ERROR: Missing type for entry '%s' kind '%s'" 1200 % (kwargs.get('name'), kwargs.get('kind')), file=sys.stderr) 1201 1202 # Attributes are Read-Only, but edges may be mutated by 1203 # Metadata, particularly during construct_graph 1204 1205 self._name = kwargs['name'] 1206 self._type = kwargs['type'] 1207 self._kind = kwargs['kind'] # static, dynamic, or controls 1208 1209 self._init_common(**kwargs) 1210 1211 @property 1212 def type(self): 1213 return self._type 1214 1215 @property 1216 def kind(self): 1217 return self._kind 1218 1219 @property 1220 def hal_major_version(self): 1221 return self._hal_major_version 1222 1223 @property 1224 def hal_minor_version(self): 1225 return self._hal_minor_version 1226 1227 @property 1228 def visibility(self): 1229 return self._visibility 1230 1231 @property 1232 def applied_visibility(self): 1233 return self._visibility or 'system' 1234 1235 @property 1236 def applied_ndk_visible(self): 1237 if self._visibility in ("public", "ndk_public"): 1238 return "true" 1239 return "false" 1240 1241 @property 1242 def synthetic(self): 1243 return self._synthetic 1244 1245 @property 1246 def hwlevel(self): 1247 return self._hwlevel 1248 1249 @property 1250 def deprecated(self): 1251 return self._deprecated 1252 1253 @property 1254 def deprecation_description(self): 1255 return self._deprecation_description 1256 1257 @property 1258 def permission_needed(self): 1259 return self._permission_needed or "false" 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._permission_needed = kwargs.get('permission_needed') 1421 self._optional = kwargs.get('optional') 1422 self._ndk_visible = kwargs.get('ndk_visible') 1423 1424 self._property_keys = kwargs 1425 1426 def merge(self): 1427 """ 1428 Copy the attributes into a new entry, merging it with the target entry 1429 if it's a clone. 1430 """ 1431 return MergedEntry(self) 1432 1433 # Helpers for accessing less than the fully qualified name 1434 1435 def get_name_as_list(self): 1436 """ 1437 Returns the name as a list split by a period. 1438 1439 For example: 1440 entry.name is 'android.lens.info.shading' 1441 entry.get_name_as_list() == ['android', 'lens', 'info', 'shading'] 1442 """ 1443 return self.name.split(".") 1444 1445 def get_inner_namespace_list(self): 1446 """ 1447 Returns the inner namespace part of the name as a list 1448 1449 For example: 1450 entry.name is 'android.lens.info.shading' 1451 entry.get_inner_namespace_list() == ['info'] 1452 """ 1453 return self.get_name_as_list()[2:-1] 1454 1455 def get_outer_namespace(self): 1456 """ 1457 Returns the outer namespace as a string. 1458 1459 For example: 1460 entry.name is 'android.lens.info.shading' 1461 entry.get_outer_namespace() == 'android' 1462 1463 Remarks: 1464 Since outer namespaces are non-recursive, 1465 and each entry has one, this does not need to be a list. 1466 """ 1467 return self.get_name_as_list()[0] 1468 1469 def get_section(self): 1470 """ 1471 Returns the section as a string. 1472 1473 For example: 1474 entry.name is 'android.lens.info.shading' 1475 entry.get_section() == '' 1476 1477 Remarks: 1478 Since outer namespaces are non-recursive, 1479 and each entry has one, this does not need to be a list. 1480 """ 1481 return self.get_name_as_list()[1] 1482 1483 def get_name_minimal(self): 1484 """ 1485 Returns only the last component of the fully qualified name as a string. 1486 1487 For example: 1488 entry.name is 'android.lens.info.shading' 1489 entry.get_name_minimal() == 'shading' 1490 1491 Remarks: 1492 entry.name_short it an alias for this 1493 """ 1494 return self.get_name_as_list()[-1] 1495 1496 def get_path_without_name(self): 1497 """ 1498 Returns a string path to the entry, with the name component excluded. 1499 1500 For example: 1501 entry.name is 'android.lens.info.shading' 1502 entry.get_path_without_name() == 'android.lens.info' 1503 """ 1504 return ".".join(self.get_name_as_list()[0:-1]) 1505 1506 1507class Clone(Entry): 1508 """ 1509 A Node corresponding to a <clone> element. It has all the attributes of an 1510 <entry> element (Entry) plus the additions specified below. 1511 1512 Attributes (Read-Only): 1513 entry: an edge to an Entry object that this targets 1514 target_kind: A string describing the kind of the target entry. 1515 name: a string of the name, same as entry.name 1516 kind: a string of the Kind ancestor, one of 'static', 'controls', 'dynamic' 1517 for the <clone> element. 1518 type: always None, since a clone cannot override the type. 1519 """ 1520 def __init__(self, entry=None, **kwargs): 1521 """ 1522 Instantiate a new Clone node. 1523 1524 Args: 1525 name: A string with the fully qualified name, e.g. 'android.shading.mode' 1526 type: A string describing the type, e.g. 'int32' 1527 kind: A string describing the kind, e.g. 'static' 1528 target_kind: A string for the kind of the target entry, e.g. 'dynamic' 1529 hal_version: A string for the initial HIDL HAL metadata version this entry 1530 was added in 1531 1532 Args (if container): 1533 container: A string describing the container, e.g. 'array' or 'tuple' 1534 container_sizes: A list of string sizes if a container, or None otherwise 1535 1536 Args (if container is 'tuple'): 1537 tuple_values: A list of tuple values, e.g. ['width', 'height'] 1538 1539 Args (if the 'enum' attribute is true): 1540 enum: A boolean, True if this is an enum, False otherwise 1541 enum_values: A list of value strings, e.g. ['ON', 'OFF'] 1542 enum_optionals: A list of optional enum values, e.g. ['OFF'] 1543 enum_notes: A dictionary of value->notes strings. 1544 enum_ids: A dictionary of value->id strings. 1545 1546 Args (optional): 1547 entry: An edge to the corresponding target Entry. 1548 description: A string with a description of the entry. 1549 range: A string with the range of the values of the entry, e.g. '>= 0' 1550 units: A string with the units of the values, e.g. 'inches' 1551 details: A string with the detailed documentation for the entry 1552 hal_details: A string with the HAL implementation details for the entry 1553 ndk_details: A string with the extra NDK documentation for the entry 1554 tag_ids: A list of tag ID strings, e.g. ['BC', 'V1'] 1555 type_notes: A string with the notes for the type 1556 1557 Remarks: 1558 Note that type is not specified since it has to be the same as the 1559 entry.type. 1560 """ 1561 self._entry = entry # Entry object 1562 self._target_kind = kwargs['target_kind'] 1563 self._name = kwargs['name'] # same as entry.name 1564 self._kind = kwargs['kind'] 1565 1566 # illegal to override the type, it should be the same as the entry 1567 self._type = None 1568 # the rest of the kwargs are optional 1569 # can be used to override the regular entry data 1570 self._init_common(**kwargs) 1571 1572 @property 1573 def entry(self): 1574 return self._entry 1575 1576 @property 1577 def target_kind(self): 1578 return self._target_kind 1579 1580 def is_clone(self): 1581 """ 1582 Whether or not this is a Clone instance. 1583 1584 Returns: 1585 True 1586 """ 1587 return True 1588 1589class MergedEntry(Entry): 1590 """ 1591 A MergedEntry has all the attributes of a Clone and its target Entry merged 1592 together. 1593 1594 Remarks: 1595 Useful when we want to 'unfold' a clone into a real entry by copying out 1596 the target entry data. In this case we don't care about distinguishing 1597 a clone vs an entry. 1598 """ 1599 def __init__(self, entry): 1600 """ 1601 Create a new instance of MergedEntry. 1602 1603 Args: 1604 entry: An Entry or Clone instance 1605 """ 1606 props_distinct = ['description', 'units', 'range', 'details', 1607 'hal_details', 'ndk_details', 'tags', 'kind', 1608 'deprecation_description'] 1609 1610 for p in props_distinct: 1611 p = '_' + p 1612 if entry.is_clone(): 1613 setattr(self, p, getattr(entry, p) or getattr(entry.entry, p)) 1614 else: 1615 setattr(self, p, getattr(entry, p)) 1616 1617 props_common = ['parent', 'name', 'container', 1618 'container_sizes', 'enum', 1619 'tuple_values', 1620 'type', 1621 'type_notes', 1622 'visibility', 1623 'ndk_visible', 1624 'synthetic', 1625 'hwlevel', 1626 'deprecated', 1627 'optional', 1628 'typedef', 1629 'hal_major_version', 1630 'hal_minor_version', 1631 'permission_needed' 1632 ] 1633 1634 for p in props_common: 1635 p = '_' + p 1636 if entry.is_clone(): 1637 setattr(self, p, getattr(entry.entry, p)) 1638 else: 1639 setattr(self, p, getattr(entry, p)) 1640