195640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org#!/usr/bin/env python 295640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org# Copyright (c) 2012 The Chromium Authors. All rights reserved. 301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Use of this source code is governed by a BSD-style license that can be 401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# found in the LICENSE file. 501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''Base types for nodes in a GRIT resource tree. 701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org''' 801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 9977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.orgimport ast 1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport os 1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport types 1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom xml.sax import saxutils 1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 14ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.orgfrom grit import clique 1501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import exception 1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import util 1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 19ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.orgclass Node(object): 20ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org '''An item in the tree that has children.''' 2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Valid content types that can be returned by _ContentType() 2301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org _CONTENT_TYPE_NONE = 0 # No CDATA content but may have children 2401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org _CONTENT_TYPE_CDATA = 1 # Only CDATA, no children. 2501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org _CONTENT_TYPE_MIXED = 2 # CDATA and children, possibly intermingled 2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Default nodes to not whitelist skipped 2801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org _whitelist_marked_as_skip = False 2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 30977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org # A class-static cache to speed up EvaluateExpression(). 31977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org # Keys are expressions (e.g. 'is_ios and lang == "fr"'). Values are tuples 32977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org # (code, variables_in_expr) where code is the compiled expression and can be 33977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org # directly eval'd, and variables_in_expr is the list of variable and method 34977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org # names used in the expression (e.g. ['is_ios', 'lang']). 35977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org eval_expr_cache = {} 3651dde08ce1ace3fba4e6d64ad401de1f4f45b225joi@chromium.org 3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def __init__(self): 3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.children = [] # A list of child elements 3901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content = [] # A list of u'' and/or child elements (this 4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # duplicates 'children' but 4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # is needed to preserve markup-type content). 4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.name = u'' # The name of this element 4301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.attrs = {} # The set of attributes (keys to values) 4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.parent = None # Our parent unless we are the root element. 4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.uberclique = None # Allows overriding uberclique for parts of tree 4601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 47ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org # This context handler allows you to write "with node:" and get a 48ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org # line identifying the offending node if an exception escapes from the body 49ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org # of the with statement. 50ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org def __enter__(self): 51ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org return self 52ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org 53ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org def __exit__(self, exc_type, exc_value, traceback): 54ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org if exc_type is not None: 55ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org print u'Error processing node %s' % unicode(self) 56ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org 5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def __iter__(self): 58ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org '''A preorder iteration through the tree that this node is the root of.''' 59ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org return self.Preorder() 6001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 61ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org def Preorder(self): 6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Generator that generates first this node, then the same generator for 6301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org any child nodes.''' 6401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org yield self 6501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for child in self.children: 66ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org for iterchild in child.Preorder(): 6701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org yield iterchild 6801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 69ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org def ActiveChildren(self): 70ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org '''Returns the children of this node that should be included in the current 71ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org configuration. Overridden by <if>.''' 72ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org return [node for node in self.children if not node.WhitelistMarkedAsSkip()] 73ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org 74ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org def ActiveDescendants(self): 75ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org '''Yields the current node and all descendants that should be included in 76ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org the current configuration, in preorder.''' 77ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org yield self 78ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org for child in self.ActiveChildren(): 79ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org for descendant in child.ActiveDescendants(): 80ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org yield descendant 81ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org 8201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetRoot(self): 8301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the root Node in the tree this Node belongs to.''' 8401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org curr = self 8501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org while curr.parent: 8601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org curr = curr.parent 8701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return curr 8801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 8901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # TODO(joi) Use this (currently untested) optimization?: 9001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org #if hasattr(self, '_root'): 9101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # return self._root 9201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org #curr = self 9301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org #while curr.parent and not hasattr(curr, '_root'): 9401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # curr = curr.parent 9501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org #if curr.parent: 9601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # self._root = curr._root 9701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org #else: 9801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # self._root = curr 9901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org #return self._root 10001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 10101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def StartParsing(self, name, parent): 10201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Called at the start of parsing. 10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org name: u'elementname' 10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org parent: grit.node.base.Node or subclass or None 10701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert isinstance(name, types.StringTypes) 10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert not parent or isinstance(parent, Node) 11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.name = name 11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.parent = parent 11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 11301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def AddChild(self, child): 11401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Adds a child to the list of children of this node, if it is a valid 11501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org child for the node.''' 11601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert isinstance(child, Node) 11701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (not self._IsValidChild(child) or 11801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self._ContentType() == self._CONTENT_TYPE_CDATA): 11925fe2049cd4e0348041b7ed636dfe7682ff91021joi@chromium.org explanation = 'invalid child %s for parent %s' % (str(child), self.name) 12001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.UnexpectedChild(explanation) 12101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.children.append(child) 12201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content.append(child) 12301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 12401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def RemoveChild(self, child_id): 12501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Removes the first node that has a "name" attribute which 12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org matches "child_id" in the list of immediate children of 12701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org this node. 12801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 12901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 13001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org child_id: String identifying the child to be removed 13101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 13201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org index = 0 13301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Safe not to copy since we only remove the first element found 13401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for child in self.children: 13501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org name_attr = child.attrs['name'] 13601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if name_attr == child_id: 13701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.children.pop(index) 13801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content.pop(index) 13901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org break 14001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org index += 1 14101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 14201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def AppendContent(self, content): 14301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Appends a chunk of text as content of this node. 14401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 14501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 14601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org content: u'hello' 14701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 14801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Return: 14901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org None 15001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 15101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert isinstance(content, types.StringTypes) 15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self._ContentType() != self._CONTENT_TYPE_NONE: 15301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content.append(content) 15401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org elif content.strip() != '': 15501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.UnexpectedContent() 15601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 15701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def HandleAttribute(self, attrib, value): 15801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Informs the node of an attribute that was parsed out of the GRD file 15901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for it. 16001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 16101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 16201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org attrib: 'name' 16301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org value: 'fooblat' 16401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 16501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Return: 16601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org None 16701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 16801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert isinstance(attrib, types.StringTypes) 16901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert isinstance(value, types.StringTypes) 17001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self._IsValidAttribute(attrib, value): 17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.attrs[attrib] = value 17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 17301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.UnexpectedAttribute(attrib) 17401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def EndParsing(self): 17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Called at the end of parsing.''' 17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 17801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # TODO(joi) Rewrite this, it's extremely ugly! 17901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(self.mixed_content): 18001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if isinstance(self.mixed_content[0], types.StringTypes): 18101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Remove leading and trailing chunks of pure whitespace. 18201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org while (len(self.mixed_content) and 18301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org isinstance(self.mixed_content[0], types.StringTypes) and 18401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content[0].strip() == ''): 18501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content = self.mixed_content[1:] 18601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Strip leading and trailing whitespace from mixed content chunks 18701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # at front and back. 18801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (len(self.mixed_content) and 18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org isinstance(self.mixed_content[0], types.StringTypes)): 19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content[0] = self.mixed_content[0].lstrip() 19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Remove leading and trailing ''' (used to demarcate whitespace) 19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (len(self.mixed_content) and 19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org isinstance(self.mixed_content[0], types.StringTypes)): 19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.mixed_content[0].startswith("'''"): 19501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content[0] = self.mixed_content[0][3:] 19601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(self.mixed_content): 19701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if isinstance(self.mixed_content[-1], types.StringTypes): 19801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Same stuff all over again for the tail end. 19901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org while (len(self.mixed_content) and 20001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org isinstance(self.mixed_content[-1], types.StringTypes) and 20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content[-1].strip() == ''): 20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content = self.mixed_content[:-1] 20301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (len(self.mixed_content) and 20401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org isinstance(self.mixed_content[-1], types.StringTypes)): 20501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content[-1] = self.mixed_content[-1].rstrip() 20601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (len(self.mixed_content) and 20701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org isinstance(self.mixed_content[-1], types.StringTypes)): 20801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.mixed_content[-1].endswith("'''"): 20901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.mixed_content[-1] = self.mixed_content[-1][:-3] 21001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 21101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Check that all mandatory attributes are there. 21201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for node_mandatt in self.MandatoryAttributes(): 21301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org mandatt_list = [] 21401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if node_mandatt.find('|') >= 0: 21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org mandatt_list = node_mandatt.split('|') 21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 21701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org mandatt_list.append(node_mandatt) 21801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org mandatt_option_found = False 22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for mandatt in mandatt_list: 22101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert mandatt not in self.DefaultAttributes().keys() 22201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if mandatt in self.attrs: 22301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not mandatt_option_found: 22401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org mandatt_option_found = True 22501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 22601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.MutuallyExclusiveMandatoryAttribute(mandatt) 22701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 22801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not mandatt_option_found: 22901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.MissingMandatoryAttribute(mandatt) 23001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 23101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Add default attributes if not specified in input file. 23201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for defattr in self.DefaultAttributes(): 23301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not defattr in self.attrs: 23401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.attrs[defattr] = self.DefaultAttributes()[defattr] 23501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 23601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetCdata(self): 23701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns all CDATA of this element, concatenated into a single 23801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org string. Note that this ignores any elements embedded in CDATA.''' 239705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org return ''.join([c for c in self.mixed_content 240705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org if isinstance(c, types.StringTypes)]) 24101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 24201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def __unicode__(self): 24301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns this node and all nodes below it as an XML document in a Unicode 24401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org string.''' 24501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org header = u'<?xml version="1.0" encoding="UTF-8"?>\n' 24601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return header + self.FormatXml() 24701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 24801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def FormatXml(self, indent = u'', one_line = False): 24901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns this node and all nodes below it as an XML 25001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org element in a Unicode string. This differs from __unicode__ in that it does 25101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org not include the <?xml> stuff at the top of the string. If one_line is true, 25201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org children and CDATA are layed out in a way that preserves internal 25301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org whitespace. 25401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 25501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert isinstance(indent, types.StringTypes) 25601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 25701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org content_one_line = (one_line or 25801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self._ContentType() == self._CONTENT_TYPE_MIXED) 25901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org inside_content = self.ContentsAsXml(indent, content_one_line) 26001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 26101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Then the attributes for this node. 26283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org attribs = u'' 26383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org default_attribs = self.DefaultAttributes() 26483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org for attrib, value in sorted(self.attrs.items()): 26501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Only print an attribute if it is other than the default value. 26683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if attrib not in default_attribs or value != default_attribs[attrib]: 26783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org attribs += u' %s=%s' % (attrib, saxutils.quoteattr(value)) 26801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 26901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Finally build the XML for our node and return it 27001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(inside_content) > 0: 27101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if one_line: 27201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return u'<%s%s>%s</%s>' % (self.name, attribs, inside_content, self.name) 27301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org elif content_one_line: 27401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return u'%s<%s%s>\n%s %s\n%s</%s>' % ( 27501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org indent, self.name, attribs, 27601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org indent, inside_content, 27701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org indent, self.name) 27801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 27901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return u'%s<%s%s>\n%s\n%s</%s>' % ( 28001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org indent, self.name, attribs, 28101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org inside_content, 28201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org indent, self.name) 28301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 28401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return u'%s<%s%s />' % (indent, self.name, attribs) 28501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 28601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ContentsAsXml(self, indent, one_line): 28701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the contents of this node (CDATA and child elements) in XML 28801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org format. If 'one_line' is true, the content will be laid out on one line.''' 28901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert isinstance(indent, types.StringTypes) 29001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 29101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Build the contents of the element. 29201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org inside_parts = [] 29301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org last_item = None 29401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for mixed_item in self.mixed_content: 29501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if isinstance(mixed_item, Node): 29601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org inside_parts.append(mixed_item.FormatXml(indent + u' ', one_line)) 29701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not one_line: 29801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org inside_parts.append(u'\n') 29901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 30001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org message = mixed_item 30101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # If this is the first item and it starts with whitespace, we add 30201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # the ''' delimiter. 30301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not last_item and message.lstrip() != message: 30401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org message = u"'''" + message 30501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org inside_parts.append(util.EncodeCdata(message)) 30601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org last_item = mixed_item 30701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 30801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # If there are only child nodes and no cdata, there will be a spurious 30901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # trailing \n 31001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(inside_parts) and inside_parts[-1] == '\n': 31101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org inside_parts = inside_parts[:-1] 31201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 31301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # If the last item is a string (not a node) and ends with whitespace, 31401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # we need to add the ''' delimiter. 31501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (isinstance(last_item, types.StringTypes) and 31601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org last_item.rstrip() != last_item): 31701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org inside_parts[-1] = inside_parts[-1] + u"'''" 31801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 31901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return u''.join(inside_parts) 32001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 32177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org def SubstituteMessages(self, substituter): 32277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org '''Applies substitutions to all messages in the tree. 32377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org 32477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org Called as a final step of RunGatherers. 32577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org 32677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org Args: 32777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org substituter: a grit.util.Substituter object. 32877cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org ''' 32977cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org for child in self.children: 33077cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org child.SubstituteMessages(substituter) 33177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org 33201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def _IsValidChild(self, child): 33301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns true if 'child' is a valid child of this node. 33401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Overridden by subclasses.''' 33501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return False 33601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 33701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def _IsValidAttribute(self, name, value): 33801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns true if 'name' is the name of a valid attribute of this element 33901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org and 'value' is a valid value for that attribute. Overriden by 34001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org subclasses unless they have only mandatory attributes.''' 34101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return (name in self.MandatoryAttributes() or 34201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org name in self.DefaultAttributes()) 34301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 34401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def _ContentType(self): 34501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the type of content this element can have. Overridden by 34601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org subclasses. The content type can be one of the _CONTENT_TYPE_XXX constants 34701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org above.''' 34801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self._CONTENT_TYPE_NONE 34901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 35001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def MandatoryAttributes(self): 35101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns a list of attribute names that are mandatory (non-optional) 35201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org on the current element. One can specify a list of 35301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org "mutually exclusive mandatory" attributes by specifying them as one 35401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org element in the list, separated by a "|" character. 35501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 35601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return [] 35701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 35801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def DefaultAttributes(self): 35901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns a dictionary of attribute names that have defaults, mapped to 36001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org the default value. Overridden by subclasses.''' 36101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return {} 36201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 36301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetCliques(self): 36401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns all MessageClique objects belonging to this node. Overridden 36501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org by subclasses. 36601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 36701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Return: 36801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org [clique1, clique2] or [] 36901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 37001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return [] 37101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 37201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ToRealPath(self, path_from_basedir): 37301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns a real path (which can be absolute or relative to the current 37401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org working directory), given a path that is relative to the base directory 37501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org set for the GRIT input file. 37601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 37701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 37801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org path_from_basedir: '..' 37901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 38001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Return: 38101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 'resource' 38201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 38301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return util.normpath(os.path.join(self.GetRoot().GetBaseDir(), 384bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org os.path.expandvars(path_from_basedir))) 38501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 386ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org def GetInputPath(self): 387ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org '''Returns a path, relative to the base directory set for the grd file, 388ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org that points to the file the node refers to. 389ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org ''' 390ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org # This implementation works for most nodes that have an input file. 391ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org return self.attrs['file'] 39201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 39301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def UberClique(self): 39401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the uberclique that should be used for messages originating in 39501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org a given node. If the node itself has its uberclique set, that is what we 39601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org use, otherwise we search upwards until we find one. If we do not find one 39701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org even at the root node, we set the root node's uberclique to a new 39801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org uberclique instance. 39901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 40001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node = self 40101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org while not node.uberclique and node.parent: 40201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node = node.parent 40301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not node.uberclique: 40401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node.uberclique = clique.UberClique() 40501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return node.uberclique 40601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 40701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def IsTranslateable(self): 40801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns false if the node has contents that should not be translated, 40901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org otherwise returns false (even if the node has no contents). 41001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 41101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not 'translateable' in self.attrs: 41201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return True 41301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 41401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.attrs['translateable'] == 'true' 41501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 41601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetNodeById(self, id): 41701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the node in the subtree parented by this node that has a 'name' 41801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org attribute matching 'id'. Returns None if no such node is found. 41901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 42001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for node in self: 42101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if 'name' in node.attrs and node.attrs['name'] == id: 42201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return node 42301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return None 42401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 425705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org def GetChildrenOfType(self, type): 426705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org '''Returns a list of all subnodes (recursing to all leaves) of this node 42783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org that are of the indicated type (or tuple of types). 428705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org 429705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org Args: 430705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org type: A type you could use with isinstance(). 431705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org 432705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org Return: 433705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org A list, possibly empty. 434705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org ''' 435705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org return [child for child in self if isinstance(child, type)] 436705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org 43701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetTextualIds(self): 438ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org '''Returns a list of the textual ids of this node. 43901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 44001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if 'name' in self.attrs: 44101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return [self.attrs['name']] 442ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org return [] 44301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 44451dde08ce1ace3fba4e6d64ad401de1f4f45b225joi@chromium.org @classmethod 445977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org def EvaluateExpression(cls, expr, defs, target_platform, extra_variables={}): 446ca6c225d0059dd17b476c934e744a3c4f60e2df2joi@chromium.org '''Worker for EvaluateCondition (below) and conditions in XTB files.''' 447977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org if expr in cls.eval_expr_cache: 448977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org code, variables_in_expr = cls.eval_expr_cache[expr] 449977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org else: 450977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org # Get a list of all variable and method names used in the expression. 451977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org syntax_tree = ast.parse(expr, mode='eval') 452977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org variables_in_expr = [node.id for node in ast.walk(syntax_tree) if 453977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org isinstance(node, ast.Name) and node.id not in ('True', 'False')] 454977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org code = compile(syntax_tree, filename='<string>', mode='eval') 455977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org cls.eval_expr_cache[expr] = code, variables_in_expr 456977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org 457977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org # Set values only for variables that are needed to eval the expression. 458977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org variable_map = {} 459977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org for name in variables_in_expr: 460977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org if name == 'os': 461977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = target_platform 462977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'defs': 463977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = defs 464977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org 465977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'is_linux': 466977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = target_platform.startswith('linux') 467977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'is_macosx': 468977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = target_platform == 'darwin' 469977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'is_win': 470977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = target_platform in ('cygwin', 'win32') 471977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'is_android': 472977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = target_platform == 'android' 473977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'is_ios': 474977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = target_platform == 'ios' 475a462d172a329c260c4b77186af3e9813538e6267joi@chromium.org elif name == 'is_bsd': 476a462d172a329c260c4b77186af3e9813538e6267joi@chromium.org value = 'bsd' in target_platform 477977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'is_posix': 478977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = (target_platform in ('darwin', 'linux2', 'linux3', 'sunos5', 479977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org 'android', 'ios') 480977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org or 'bsd' in target_platform) 481977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org 482977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'pp_ifdef': 483977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org def pp_ifdef(symbol): 484977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org return symbol in defs 485977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = pp_ifdef 486977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name == 'pp_if': 487977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org def pp_if(symbol): 488977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org return defs.get(symbol, False) 489977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = pp_if 490977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org 491977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name in defs: 492977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = defs[name] 493977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org elif name in extra_variables: 494977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = extra_variables[name] 495977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org else: 496977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org # Undefined variables default to False. 497977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org value = False 49819bf7c06d3192b2ac2fb7a7dd9775bc677636e3ejoi@chromium.org 499977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org variable_map[name] = value 500977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org 501977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org eval_result = eval(code, {}, variable_map) 502977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org assert isinstance(eval_result, bool) 50351dde08ce1ace3fba4e6d64ad401de1f4f45b225joi@chromium.org return eval_result 504ca6c225d0059dd17b476c934e744a3c4f60e2df2joi@chromium.org 50501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def EvaluateCondition(self, expr): 50601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns true if and only if the Python expression 'expr' evaluates 50701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org to true. 50801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 50901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org The expression is given a few local variables: 51001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org - 'lang' is the language currently being output 511ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org (the 'lang' attribute of the <output> element). 512ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org - 'context' is the current output context 513ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org (the 'context' attribute of the <output> element). 514ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org - 'defs' is a map of C preprocessor-style symbol names to their values. 51501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org - 'os' is the current platform (likely 'linux2', 'win32' or 'darwin'). 516ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org - 'pp_ifdef(symbol)' is a shorthand for "symbol in defs". 517ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org - 'pp_if(symbol)' is a shorthand for "symbol in defs and defs[symbol]". 518ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org - 'is_linux', 'is_macosx', 'is_win', 'is_posix' are true if 'os' 519ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org matches the given platform. 52001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 52101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org root = self.GetRoot() 522ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org lang = getattr(root, 'output_language', '') 523ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org context = getattr(root, 'output_context', '') 524ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org defs = getattr(root, 'defines', {}) 525abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org target_platform = getattr(root, 'target_platform', '') 526977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org extra_variables = { 527977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org 'lang': lang, 528977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org 'context': context, 529977eb64972b3d1f6e24e878fc0858cb33aef25e2newt@chromium.org } 530ca6c225d0059dd17b476c934e744a3c4f60e2df2joi@chromium.org return Node.EvaluateExpression( 531ca6c225d0059dd17b476c934e744a3c4f60e2df2joi@chromium.org expr, defs, target_platform, extra_variables) 53201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 53301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def OnlyTheseTranslations(self, languages): 53401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Turns off loading of translations for languages not in the provided list. 53501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 53601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Attrs: 53701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org languages: ['fr', 'zh_cn'] 53801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 53901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for node in self: 54001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (hasattr(node, 'IsTranslation') and 54101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node.IsTranslation() and 54201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node.GetLang() not in languages): 54301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node.DisableLoading() 54401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 545ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org def FindBooleanAttribute(self, attr, default, skip_self): 546ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org '''Searches all ancestors of the current node for the nearest enclosing 547ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org definition of the given boolean attribute. 548ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org 549ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org Args: 550ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org attr: 'fallback_to_english' 551ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org default: What to return if no node defines the attribute. 552ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org skip_self: Don't check the current node, only its parents. 553ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org ''' 554ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org p = self.parent if skip_self else self 555ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org while p: 556ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org value = p.attrs.get(attr, 'default').lower() 557ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org if value != 'default': 558ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org return (value == 'true') 559ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org p = p.parent 560ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org return default 561ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org 56201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def PseudoIsAllowed(self): 56301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns true if this node is allowed to use pseudo-translations. This 56401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org is true by default, unless this node is within a <release> node that has 56501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org the allow_pseudo attribute set to false. 56601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 567ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org return self.FindBooleanAttribute('allow_pseudo', 568ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org default=True, skip_self=True) 56901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 57001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ShouldFallbackToEnglish(self): 57101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns true iff this node should fall back to English when 57201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org pseudotranslations are disabled and no translation is available for a 57301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org given message. 57401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 575ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org return self.FindBooleanAttribute('fallback_to_english', 576ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org default=False, skip_self=True) 57701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 57801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def WhitelistMarkedAsSkip(self): 57901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns true if the node is marked to be skipped in the output by a 58001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org whitelist. 58101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 58201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self._whitelist_marked_as_skip 58301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 58401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def SetWhitelistMarkedAsSkip(self, mark_skipped): 58501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Sets WhitelistMarkedAsSkip. 58601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 58701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self._whitelist_marked_as_skip = mark_skipped 58801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 58977cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org def ExpandVariables(self): 59077cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org '''Whether we need to expand variables on a given node.''' 59177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org return False 59277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org 59301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 59401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass ContentNode(Node): 59501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Convenience baseclass for nodes that can have content.''' 59601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def _ContentType(self): 59701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self._CONTENT_TYPE_MIXED 59801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 599