Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Unified Diff: grit/node/misc.py

Issue 7994004: Initial source commit to grit-i18n project. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « grit/node/message_unittest.py ('k') | grit/node/misc_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « grit/node/message_unittest.py ('k') | grit/node/misc_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698