| Index: grit/node/misc.py
|
| ===================================================================
|
| --- grit/node/misc.py (revision 0)
|
| +++ grit/node/misc.py (revision 0)
|
| @@ -0,0 +1,401 @@
|
| +#!/usr/bin/python2.4
|
| +# Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +'''Miscellaneous node types.
|
| +'''
|
| +
|
| +import os.path
|
| +import re
|
| +import sys
|
| +
|
| +from grit.node import base
|
| +from grit.node import message
|
| +
|
| +from grit import exception
|
| +from grit import constants
|
| +from grit import util
|
| +
|
| +import grit.format.rc_header
|
| +
|
| +
|
| +def _ReadFirstIdsFromFile(filename, defines):
|
| + '''Read the starting resource id values from |filename|. We also
|
| + expand variables of the form <(FOO) based on defines passed in on
|
| + the command line.
|
| +
|
| + Returns a tuple, the absolute path of SRCDIR followed by the
|
| + first_ids dictionary.
|
| + '''
|
| + first_ids_dict = eval(open(filename).read())
|
| +
|
| + # TODO(joi@chromium.org): It might make sense to make this a
|
| + # parameter of the .grd file rather than of the resource_ids file.
|
| + src_root_dir = os.path.abspath(os.path.join(os.path.dirname(filename),
|
| + first_ids_dict['SRCDIR']))
|
| +
|
| + def ReplaceVariable(matchobj):
|
| + for key, value in defines.iteritems():
|
| + if matchobj.group(1) == key:
|
| + value = os.path.join(src_root_dir, value)
|
| + value = os.path.abspath(value)[len(src_root_dir) + 1:]
|
| + return value
|
| + return ''
|
| +
|
| + renames = []
|
| + for grd_filename in first_ids_dict:
|
| + new_grd_filename = re.sub(r'<\(([A-Za-z_]+)\)', ReplaceVariable,
|
| + grd_filename)
|
| + if new_grd_filename != grd_filename:
|
| + new_grd_filename = new_grd_filename.replace('\\', '/')
|
| + renames.append((grd_filename, new_grd_filename))
|
| +
|
| + for grd_filename, new_grd_filename in renames:
|
| + first_ids_dict[new_grd_filename] = first_ids_dict[grd_filename]
|
| + del(first_ids_dict[grd_filename])
|
| +
|
| + return (src_root_dir, first_ids_dict)
|
| +
|
| +
|
| +class IfNode(base.Node):
|
| + '''A node for conditional inclusion of resources.
|
| + '''
|
| +
|
| + def _IsValidChild(self, child):
|
| + from grit.node import empty
|
| + assert self.parent, '<if> node should never be root.'
|
| + if isinstance(self.parent, empty.IncludesNode):
|
| + from grit.node import include
|
| + return isinstance(child, include.IncludeNode)
|
| + elif isinstance(self.parent, empty.MessagesNode):
|
| + from grit.node import message
|
| + return isinstance(child, message.MessageNode)
|
| + elif isinstance(self.parent, empty.StructuresNode):
|
| + from grit.node import structure
|
| + return isinstance(child, structure.StructureNode)
|
| + elif isinstance(self.parent, empty.OutputsNode):
|
| + from grit.node import io
|
| + return isinstance(child, io.OutputNode)
|
| + elif isinstance(self.parent, empty.TranslationsNode):
|
| + from grit.node import io
|
| + return isinstance(child, io.FileNode)
|
| + else:
|
| + return False
|
| +
|
| + def MandatoryAttributes(self):
|
| + return ['expr']
|
| +
|
| + def IsConditionSatisfied(self):
|
| + '''Returns true if and only if the Python expression stored in attribute
|
| + 'expr' evaluates to true.
|
| + '''
|
| + return self.EvaluateCondition(self.attrs['expr'])
|
| +
|
| +
|
| +class ReleaseNode(base.Node):
|
| + '''The <release> element.'''
|
| +
|
| + def _IsValidChild(self, child):
|
| + from grit.node import empty
|
| + return isinstance(child, (empty.IncludesNode, empty.MessagesNode,
|
| + empty.StructuresNode, empty.IdentifiersNode))
|
| +
|
| + def _IsValidAttribute(self, name, value):
|
| + return (
|
| + (name == 'seq' and int(value) <= self.GetRoot().GetCurrentRelease()) or
|
| + name == 'allow_pseudo'
|
| + )
|
| +
|
| + def MandatoryAttributes(self):
|
| + return ['seq']
|
| +
|
| + def DefaultAttributes(self):
|
| + return { 'allow_pseudo' : 'true' }
|
| +
|
| + def GetReleaseNumber():
|
| + '''Returns the sequence number of this release.'''
|
| + return self.attribs['seq']
|
| +
|
| + def ItemFormatter(self, t):
|
| + if t == 'data_package':
|
| + from grit.format import data_pack
|
| + return data_pack.DataPack()
|
| + else:
|
| + return super(type(self), self).ItemFormatter(t)
|
| +
|
| +class GritNode(base.Node):
|
| + '''The <grit> root element.'''
|
| +
|
| + def __init__(self):
|
| + base.Node.__init__(self)
|
| + self.output_language = ''
|
| + self.defines = {}
|
| +
|
| + def _IsValidChild(self, child):
|
| + from grit.node import empty
|
| + return isinstance(child, (ReleaseNode, empty.TranslationsNode,
|
| + empty.OutputsNode))
|
| +
|
| + def _IsValidAttribute(self, name, value):
|
| + if name not in ['base_dir', 'source_lang_id',
|
| + 'latest_public_release', 'current_release',
|
| + 'enc_check', 'tc_project']:
|
| + return False
|
| + if name in ['latest_public_release', 'current_release'] and value.strip(
|
| + '0123456789') != '':
|
| + return False
|
| + return True
|
| +
|
| + def MandatoryAttributes(self):
|
| + return ['latest_public_release', 'current_release']
|
| +
|
| + def DefaultAttributes(self):
|
| + return {
|
| + 'base_dir' : '.',
|
| + 'source_lang_id' : 'en',
|
| + 'enc_check' : constants.ENCODING_CHECK,
|
| + 'tc_project' : 'NEED_TO_SET_tc_project_ATTRIBUTE',
|
| + }
|
| +
|
| + def EndParsing(self):
|
| + base.Node.EndParsing(self)
|
| + if (int(self.attrs['latest_public_release'])
|
| + > int(self.attrs['current_release'])):
|
| + raise exception.Parsing('latest_public_release cannot have a greater '
|
| + 'value than current_release')
|
| +
|
| + self.ValidateUniqueIds()
|
| +
|
| + # Add the encoding check if it's not present (should ensure that it's always
|
| + # present in all .grd files generated by GRIT). If it's present, assert if
|
| + # it's not correct.
|
| + if 'enc_check' not in self.attrs or self.attrs['enc_check'] == '':
|
| + self.attrs['enc_check'] = constants.ENCODING_CHECK
|
| + else:
|
| + assert self.attrs['enc_check'] == constants.ENCODING_CHECK, (
|
| + 'Are you sure your .grd file is in the correct encoding (UTF-8)?')
|
| +
|
| + def ValidateUniqueIds(self):
|
| + '''Validate that 'name' attribute is unique in all nodes in this tree
|
| + except for nodes that are children of <if> nodes.
|
| + '''
|
| + unique_names = {}
|
| + duplicate_names = []
|
| + for node in self:
|
| + if isinstance(node, message.PhNode):
|
| + continue # PhNode objects have a 'name' attribute which is not an ID
|
| +
|
| + node_ids = node.GetTextualIds()
|
| + if node_ids:
|
| + for node_id in node_ids:
|
| + if util.SYSTEM_IDENTIFIERS.match(node_id):
|
| + continue # predefined IDs are sometimes used more than once
|
| +
|
| + # Don't complain about duplicate IDs if they occur in a node that is
|
| + # inside an <if> node.
|
| + if (node_id in unique_names and node_id not in duplicate_names and
|
| + (not node.parent or not isinstance(node.parent, IfNode))):
|
| + duplicate_names.append(node_id)
|
| + unique_names[node_id] = 1
|
| +
|
| + if len(duplicate_names):
|
| + raise exception.DuplicateKey(', '.join(duplicate_names))
|
| +
|
| +
|
| + def GetCurrentRelease(self):
|
| + '''Returns the current release number.'''
|
| + return int(self.attrs['current_release'])
|
| +
|
| + def GetLatestPublicRelease(self):
|
| + '''Returns the latest public release number.'''
|
| + return int(self.attrs['latest_public_release'])
|
| +
|
| + def GetSourceLanguage(self):
|
| + '''Returns the language code of the source language.'''
|
| + return self.attrs['source_lang_id']
|
| +
|
| + def GetTcProject(self):
|
| + '''Returns the name of this project in the TranslationConsole, or
|
| + 'NEED_TO_SET_tc_project_ATTRIBUTE' if it is not defined.'''
|
| + return self.attrs['tc_project']
|
| +
|
| + def SetOwnDir(self, dir):
|
| + '''Informs the 'grit' element of the directory the file it is in resides.
|
| + This allows it to calculate relative paths from the input file, which is
|
| + what we desire (rather than from the current path).
|
| +
|
| + Args:
|
| + dir: r'c:\bla'
|
| +
|
| + Return:
|
| + None
|
| + '''
|
| + assert dir
|
| + self.base_dir = os.path.normpath(os.path.join(dir, self.attrs['base_dir']))
|
| +
|
| + def GetBaseDir(self):
|
| + '''Returns the base directory, relative to the working directory. To get
|
| + the base directory as set in the .grd file, use GetOriginalBaseDir()
|
| + '''
|
| + if hasattr(self, 'base_dir'):
|
| + return self.base_dir
|
| + else:
|
| + return self.GetOriginalBaseDir()
|
| +
|
| + def GetOriginalBaseDir(self):
|
| + '''Returns the base directory, as set in the .grd file.
|
| + '''
|
| + return self.attrs['base_dir']
|
| +
|
| + def _CollectOutputFiles(self, nodes, output_files):
|
| + '''Recursively filters the list of nodes that may contain other lists
|
| + in <if> nodes, and collects all the nodes that are not enclosed by
|
| + unsatisfied <if> conditionals and not <if> nodes themselves.
|
| +
|
| + Args:
|
| + nodes: The list of nodes to filter.
|
| + output_files: The list of satisfying nodes.
|
| + '''
|
| + for node in nodes:
|
| + if node.name == 'if':
|
| + if node.IsConditionSatisfied():
|
| + self._CollectOutputFiles(node.children, output_files)
|
| + else:
|
| + output_files.append(node)
|
| +
|
| + def GetOutputFiles(self):
|
| + '''Returns the list of <output> nodes that are descendants of this node's
|
| + <outputs> child and are not enclosed by unsatisfied <if> conditionals.
|
| + '''
|
| + for child in self.children:
|
| + if child.name == 'outputs':
|
| + output_files = []
|
| + self._CollectOutputFiles(child.children, output_files)
|
| + return output_files
|
| + raise exception.MissingElement()
|
| +
|
| + def ItemFormatter(self, t):
|
| + if t == 'rc_header':
|
| + from grit.format import rc_header # import here to avoid circular dep
|
| + return rc_header.TopLevel()
|
| + elif t in ['rc_all', 'rc_translateable', 'rc_nontranslateable']:
|
| + from grit.format import rc # avoid circular dep
|
| + return rc.TopLevel()
|
| + elif t == 'resource_map_header':
|
| + from grit.format import resource_map
|
| + return resource_map.HeaderTopLevel()
|
| + elif t in ('resource_map_source', 'resource_file_map_source'):
|
| + from grit.format import resource_map
|
| + return resource_map.SourceTopLevel()
|
| + elif t == 'js_map_format':
|
| + from grit.format import js_map_format
|
| + return js_map_format.TopLevel()
|
| + elif t in ('adm', 'plist', 'plist_strings', 'admx', 'adml', 'doc', 'json',
|
| + 'reg'):
|
| + from grit.format.policy_templates import template_formatter
|
| + return template_formatter.TemplateFormatter(t)
|
| + else:
|
| + return super(type(self), self).ItemFormatter(t)
|
| +
|
| + def SetOutputContext(self, output_language, defines):
|
| + self.output_language = output_language
|
| + self.defines = defines
|
| +
|
| + def SetDefines(self, defines):
|
| + self.defines = defines
|
| +
|
| + def AssignFirstIds(self, filename_or_stream, first_id_filename, defines):
|
| + '''Assign first ids to each grouping node based on values from
|
| + tools/grit/resource_ids.'''
|
| + # If the input is a stream, then we're probably in a unit test and
|
| + # should skip this step.
|
| + if type(filename_or_stream) not in (str, unicode):
|
| + return
|
| +
|
| + # TODO(joi@chromium.org): Get rid of this hack by making it
|
| + # possible to specify the resource_ids file to use as an attribute
|
| + # of the <grit> node in the .grd file, and doing so in all Chrome
|
| + # .grd files.
|
| + #
|
| + # For now, by default, we use the the file
|
| + # ../gritsettings/resource_ids relative to grit.py.
|
| + if not first_id_filename:
|
| + first_id_filename = os.path.join(
|
| + os.path.dirname(__file__),
|
| + '..', '..', '..',
|
| + 'gritsettings', 'resource_ids')
|
| +
|
| + first_ids = None
|
| + from grit.node import empty
|
| + for node in self.inorder():
|
| + if isinstance(node, empty.GroupingNode):
|
| + if not first_ids:
|
| + src_root_dir, first_ids = _ReadFirstIdsFromFile(first_id_filename,
|
| + defines)
|
| + filename = os.path.abspath(filename_or_stream)[
|
| + len(src_root_dir) + 1:]
|
| + filename = filename.replace('\\', '/')
|
| +
|
| + # TODO(joi@chromium.org): Generalize this; users other than
|
| + # Chrome might want to use the first_id attribute; could check
|
| + # for first_ids == None to indicate not loaded, first_ids ==
|
| + # {} to indicate tried to load but found no resource_ids file.
|
| + if node.attrs['first_id'] != '':
|
| + raise Exception("Don't set the first_id attribute, update "
|
| + "%s instead." % first_id_filename)
|
| +
|
| + try:
|
| + id_list = first_ids[filename][node.name]
|
| + except KeyError, e:
|
| + print '-' * 78
|
| + print 'Resource id not set for %s (%s)!' % (filename, node.name)
|
| + print ('Please update %s to include an entry for %s. See the '
|
| + 'comments in resource_ids for information on why you need to '
|
| + 'update that file.' % (first_id_filename, filename))
|
| + print '-' * 78
|
| + raise e
|
| +
|
| + try:
|
| + node.attrs['first_id'] = str(id_list.pop(0))
|
| + except IndexError, e:
|
| + raise Exception('Please update %s and add a first id for %s (%s).'
|
| + % (first_id_filename, filename, node.name))
|
| +
|
| +
|
| +class IdentifierNode(base.Node):
|
| + '''A node for specifying identifiers that should appear in the resource
|
| + header file, and be unique amongst all other resource identifiers, but don't
|
| + have any other attributes or reference any resources.
|
| + '''
|
| +
|
| + def MandatoryAttributes(self):
|
| + return ['name']
|
| +
|
| + def DefaultAttributes(self):
|
| + return { 'comment' : '', 'id' : '' }
|
| +
|
| + def ItemFormatter(self, t):
|
| + if t == 'rc_header':
|
| + return grit.format.rc_header.Item()
|
| +
|
| + def GetId(self):
|
| + '''Returns the id of this identifier if it has one, None otherwise
|
| + '''
|
| + if 'id' in self.attrs:
|
| + return self.attrs['id']
|
| + return None
|
| +
|
| + # static method
|
| + def Construct(parent, name, id, comment):
|
| + '''Creates a new node which is a child of 'parent', with attributes set
|
| + by parameters of the same name.
|
| + '''
|
| + node = IdentifierNode()
|
| + node.StartParsing('identifier', parent)
|
| + node.HandleAttribute('name', name)
|
| + node.HandleAttribute('id', id)
|
| + node.HandleAttribute('comment', comment)
|
| + node.EndParsing()
|
| + return node
|
| + Construct = staticmethod(Construct)
|
|
|
| Property changes on: grit/node/misc.py
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|