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