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

Side by Side Diff: grit/node/misc.py

Issue 1442863002: Remove contents of grit's SVN repository. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 5 years, 1 month 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « grit/node/message_unittest.py ('k') | grit/node/misc_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Miscellaneous node types.
7 """
8
9 import os.path
10 import re
11 import sys
12
13 from grit import constants
14 from grit import exception
15 from grit import util
16 import grit.format.rc_header
17 from grit.node import base
18 from grit.node import io
19 from grit.node import message
20
21
22 # RTL languages
23 # TODO(jennyz): remove this fixed set of RTL language array
24 # now that generic expand_variable code exists.
25 _RTL_LANGS = (
26 'ar', # Arabic
27 'fa', # Farsi
28 'iw', # Hebrew
29 'ks', # Kashmiri
30 'ku', # Kurdish
31 'ps', # Pashto
32 'ur', # Urdu
33 'yi', # Yiddish
34 )
35
36
37 def _ReadFirstIdsFromFile(filename, defines):
38 """Read the starting resource id values from |filename|. We also
39 expand variables of the form <(FOO) based on defines passed in on
40 the command line.
41
42 Returns a tuple, the absolute path of SRCDIR followed by the
43 first_ids dictionary.
44 """
45 first_ids_dict = eval(util.ReadFile(filename, util.RAW_TEXT))
46 src_root_dir = os.path.abspath(os.path.join(os.path.dirname(filename),
47 first_ids_dict['SRCDIR']))
48
49 def ReplaceVariable(matchobj):
50 for key, value in defines.iteritems():
51 if matchobj.group(1) == key:
52 return value
53 return ''
54
55 renames = []
56 for grd_filename in first_ids_dict:
57 new_grd_filename = re.sub(r'<\(([A-Za-z_]+)\)', ReplaceVariable,
58 grd_filename)
59 if new_grd_filename != grd_filename:
60 abs_grd_filename = os.path.abspath(new_grd_filename)
61 if abs_grd_filename[:len(src_root_dir)] != src_root_dir:
62 new_grd_filename = os.path.basename(abs_grd_filename)
63 else:
64 new_grd_filename = abs_grd_filename[len(src_root_dir) + 1:]
65 new_grd_filename = new_grd_filename.replace('\\', '/')
66 renames.append((grd_filename, new_grd_filename))
67
68 for grd_filename, new_grd_filename in renames:
69 first_ids_dict[new_grd_filename] = first_ids_dict[grd_filename]
70 del(first_ids_dict[grd_filename])
71
72 return (src_root_dir, first_ids_dict)
73
74
75 class SplicingNode(base.Node):
76 """A node whose children should be considered to be at the same level as
77 its siblings for most purposes. This includes <if> and <part> nodes.
78 """
79
80 def _IsValidChild(self, child):
81 assert self.parent, '<%s> node should never be root.' % self.name
82 if isinstance(child, SplicingNode):
83 return True # avoid O(n^2) behavior
84 return self.parent._IsValidChild(child)
85
86
87 class IfNode(SplicingNode):
88 """A node for conditional inclusion of resources.
89 """
90
91 def MandatoryAttributes(self):
92 return ['expr']
93
94 def _IsValidChild(self, child):
95 return (isinstance(child, (ThenNode, ElseNode)) or
96 super(IfNode, self)._IsValidChild(child))
97
98 def EndParsing(self):
99 children = self.children
100 self.if_then_else = False
101 if any(isinstance(node, (ThenNode, ElseNode)) for node in children):
102 if (len(children) != 2 or not isinstance(children[0], ThenNode) or
103 not isinstance(children[1], ElseNode)):
104 raise exception.UnexpectedChild(
105 '<if> element must be <if><then>...</then><else>...</else></if>')
106 self.if_then_else = True
107
108 def ActiveChildren(self):
109 cond = self.EvaluateCondition(self.attrs['expr'])
110 if self.if_then_else:
111 return self.children[0 if cond else 1].ActiveChildren()
112 else:
113 # Equivalent to having all children inside <then> with an empty <else>
114 return super(IfNode, self).ActiveChildren() if cond else []
115
116
117 class ThenNode(SplicingNode):
118 """A <then> node. Can only appear directly inside an <if> node."""
119 pass
120
121
122 class ElseNode(SplicingNode):
123 """An <else> node. Can only appear directly inside an <if> node."""
124 pass
125
126
127 class PartNode(SplicingNode):
128 """A node for inclusion of sub-grd (*.grp) files.
129 """
130
131 def __init__(self):
132 super(PartNode, self).__init__()
133 self.started_inclusion = False
134
135 def MandatoryAttributes(self):
136 return ['file']
137
138 def _IsValidChild(self, child):
139 return self.started_inclusion and super(PartNode, self)._IsValidChild(child)
140
141
142 class ReleaseNode(base.Node):
143 """The <release> element."""
144
145 def _IsValidChild(self, child):
146 from grit.node import empty
147 return isinstance(child, (empty.IncludesNode, empty.MessagesNode,
148 empty.StructuresNode, empty.IdentifiersNode))
149
150 def _IsValidAttribute(self, name, value):
151 return (
152 (name == 'seq' and int(value) <= self.GetRoot().GetCurrentRelease()) or
153 name == 'allow_pseudo'
154 )
155
156 def MandatoryAttributes(self):
157 return ['seq']
158
159 def DefaultAttributes(self):
160 return { 'allow_pseudo' : 'true' }
161
162 def GetReleaseNumber():
163 """Returns the sequence number of this release."""
164 return self.attribs['seq']
165
166 class GritNode(base.Node):
167 """The <grit> root element."""
168
169 def __init__(self):
170 super(GritNode, self).__init__()
171 self.output_language = ''
172 self.defines = {}
173 self.substituter = None
174 self.target_platform = sys.platform
175
176 def _IsValidChild(self, child):
177 from grit.node import empty
178 return isinstance(child, (ReleaseNode, empty.TranslationsNode,
179 empty.OutputsNode))
180
181 def _IsValidAttribute(self, name, value):
182 if name not in ['base_dir', 'first_ids_file', 'source_lang_id',
183 'latest_public_release', 'current_release',
184 'enc_check', 'tc_project', 'grit_version',
185 'output_all_resource_defines', 'rc_header_format']:
186 return False
187 if name in ['latest_public_release', 'current_release'] and value.strip(
188 '0123456789') != '':
189 return False
190 return True
191
192 def MandatoryAttributes(self):
193 return ['latest_public_release', 'current_release']
194
195 def DefaultAttributes(self):
196 return {
197 'base_dir' : '.',
198 'first_ids_file': '',
199 'grit_version': 1,
200 'source_lang_id' : 'en',
201 'enc_check' : constants.ENCODING_CHECK,
202 'tc_project' : 'NEED_TO_SET_tc_project_ATTRIBUTE',
203 'output_all_resource_defines': 'true',
204 'rc_header_format': None
205 }
206
207 def EndParsing(self):
208 super(GritNode, self).EndParsing()
209 if (int(self.attrs['latest_public_release'])
210 > int(self.attrs['current_release'])):
211 raise exception.Parsing('latest_public_release cannot have a greater '
212 'value than current_release')
213
214 self.ValidateUniqueIds()
215
216 # Add the encoding check if it's not present (should ensure that it's always
217 # present in all .grd files generated by GRIT). If it's present, assert if
218 # it's not correct.
219 if 'enc_check' not in self.attrs or self.attrs['enc_check'] == '':
220 self.attrs['enc_check'] = constants.ENCODING_CHECK
221 else:
222 assert self.attrs['enc_check'] == constants.ENCODING_CHECK, (
223 'Are you sure your .grd file is in the correct encoding (UTF-8)?')
224
225 def ValidateUniqueIds(self):
226 """Validate that 'name' attribute is unique in all nodes in this tree
227 except for nodes that are children of <if> nodes.
228 """
229 unique_names = {}
230 duplicate_names = []
231 # To avoid false positives from mutually exclusive <if> clauses, check
232 # against whatever the output condition happens to be right now.
233 # TODO(benrg): do something better.
234 for node in self.ActiveDescendants():
235 if node.attrs.get('generateid', 'true') == 'false':
236 continue # Duplication not relevant in that case
237
238 for node_id in node.GetTextualIds():
239 if util.SYSTEM_IDENTIFIERS.match(node_id):
240 continue # predefined IDs are sometimes used more than once
241
242 if node_id in unique_names and node_id not in duplicate_names:
243 duplicate_names.append(node_id)
244 unique_names[node_id] = 1
245
246 if len(duplicate_names):
247 raise exception.DuplicateKey(', '.join(duplicate_names))
248
249
250 def GetCurrentRelease(self):
251 """Returns the current release number."""
252 return int(self.attrs['current_release'])
253
254 def GetLatestPublicRelease(self):
255 """Returns the latest public release number."""
256 return int(self.attrs['latest_public_release'])
257
258 def GetSourceLanguage(self):
259 """Returns the language code of the source language."""
260 return self.attrs['source_lang_id']
261
262 def GetTcProject(self):
263 """Returns the name of this project in the TranslationConsole, or
264 'NEED_TO_SET_tc_project_ATTRIBUTE' if it is not defined."""
265 return self.attrs['tc_project']
266
267 def SetOwnDir(self, dir):
268 """Informs the 'grit' element of the directory the file it is in resides.
269 This allows it to calculate relative paths from the input file, which is
270 what we desire (rather than from the current path).
271
272 Args:
273 dir: r'c:\bla'
274
275 Return:
276 None
277 """
278 assert dir
279 self.base_dir = os.path.normpath(os.path.join(dir, self.attrs['base_dir']))
280
281 def GetBaseDir(self):
282 """Returns the base directory, relative to the working directory. To get
283 the base directory as set in the .grd file, use GetOriginalBaseDir()
284 """
285 if hasattr(self, 'base_dir'):
286 return self.base_dir
287 else:
288 return self.GetOriginalBaseDir()
289
290 def GetOriginalBaseDir(self):
291 """Returns the base directory, as set in the .grd file.
292 """
293 return self.attrs['base_dir']
294
295 def SetShouldOutputAllResourceDefines(self, value):
296 """Overrides the value of output_all_resource_defines found in the grd file.
297 """
298 self.attrs['output_all_resource_defines'] = 'true' if value else 'false'
299
300 def ShouldOutputAllResourceDefines(self):
301 """Returns true if all resource defines should be output, false if
302 defines for resources not emitted to resource files should be
303 skipped.
304 """
305 return self.attrs['output_all_resource_defines'] == 'true'
306
307 def GetRcHeaderFormat(self):
308 return self.attrs['rc_header_format']
309
310 def AssignRcHeaderFormat(self, rc_header_format):
311 self.attrs['rc_header_format'] = rc_header_format
312
313 def GetInputFiles(self):
314 """Returns the list of files that are read to produce the output."""
315
316 # Importing this here avoids a circular dependency in the imports.
317 # pylint: disable-msg=C6204
318 from grit.node import include
319 from grit.node import misc
320 from grit.node import structure
321 from grit.node import variant
322
323 # Check if the input is required for any output configuration.
324 input_files = set()
325 old_output_language = self.output_language
326 for lang, ctx, fallback in self.GetConfigurations():
327 self.SetOutputLanguage(lang or self.GetSourceLanguage())
328 self.SetOutputContext(ctx)
329 self.SetFallbackToDefaultLayout(fallback)
330
331 for node in self.ActiveDescendants():
332 if isinstance(node, (io.FileNode, include.IncludeNode, misc.PartNode,
333 structure.StructureNode, variant.SkeletonNode)):
334 input_path = node.GetInputPath()
335 if input_path is not None:
336 input_files.add(self.ToRealPath(input_path))
337
338 # If it's a flattened node, grab inlined resources too.
339 if ((node.name == 'structure' or node.name == 'include')
340 and node.attrs['flattenhtml'] == 'true'):
341 if node.name == 'structure':
342 node.RunPreSubstitutionGatherer()
343 input_files.update(node.GetHtmlResourceFilenames())
344
345 self.SetOutputLanguage(old_output_language)
346 return sorted(input_files)
347
348 def GetFirstIdsFile(self):
349 """Returns a usable path to the first_ids file, if set, otherwise
350 returns None.
351
352 The first_ids_file attribute is by default relative to the
353 base_dir of the .grd file, but may be prefixed by GRIT_DIR/,
354 which makes it relative to the directory of grit.py
355 (e.g. GRIT_DIR/../gritsettings/resource_ids).
356 """
357 if not self.attrs['first_ids_file']:
358 return None
359
360 path = self.attrs['first_ids_file']
361 GRIT_DIR_PREFIX = 'GRIT_DIR'
362 if (path.startswith(GRIT_DIR_PREFIX)
363 and path[len(GRIT_DIR_PREFIX)] in ['/', '\\']):
364 return util.PathFromRoot(path[len(GRIT_DIR_PREFIX) + 1:])
365 else:
366 return self.ToRealPath(path)
367
368 def GetOutputFiles(self):
369 """Returns the list of <output> nodes that are descendants of this node's
370 <outputs> child and are not enclosed by unsatisfied <if> conditionals.
371 """
372 for child in self.children:
373 if child.name == 'outputs':
374 return [node for node in child.ActiveDescendants()
375 if node.name == 'output']
376 raise exception.MissingElement()
377
378 def GetConfigurations(self):
379 """Returns the distinct (language, context, fallback_to_default_layout)
380 triples from the output nodes.
381 """
382 return set((n.GetLanguage(), n.GetContext(), n.GetFallbackToDefaultLayout()) for n in self.GetOutputFiles())
383
384 def GetSubstitutionMessages(self):
385 """Returns the list of <message sub_variable="true"> nodes."""
386 return [n for n in self.ActiveDescendants()
387 if isinstance(n, message.MessageNode)
388 and n.attrs['sub_variable'] == 'true']
389
390 def SetOutputLanguage(self, output_language):
391 """Set the output language. Prepares substitutions.
392
393 The substitutions are reset every time the language is changed.
394 They include messages designated as variables, and language codes for html
395 and rc files.
396
397 Args:
398 output_language: a two-letter language code (eg: 'en', 'ar'...) or ''
399 """
400 if not output_language:
401 # We do not specify the output language for .grh files,
402 # so we get an empty string as the default.
403 # The value should match grit.clique.MessageClique.source_language.
404 output_language = self.GetSourceLanguage()
405 if output_language != self.output_language:
406 self.output_language = output_language
407 self.substituter = None # force recalculate
408
409 def SetOutputContext(self, output_context):
410 self.output_context = output_context
411 self.substituter = None # force recalculate
412
413 def SetFallbackToDefaultLayout(self, fallback_to_default_layout):
414 self.fallback_to_default_layout = fallback_to_default_layout
415 self.substituter = None # force recalculate
416
417 def SetDefines(self, defines):
418 self.defines = defines
419 self.substituter = None # force recalculate
420
421 def SetTargetPlatform(self, target_platform):
422 self.target_platform = target_platform
423
424 def GetSubstituter(self):
425 if self.substituter is None:
426 self.substituter = util.Substituter()
427 self.substituter.AddMessages(self.GetSubstitutionMessages(),
428 self.output_language)
429 if self.output_language in _RTL_LANGS:
430 direction = 'dir="RTL"'
431 else:
432 direction = 'dir="LTR"'
433 self.substituter.AddSubstitutions({
434 'GRITLANGCODE': self.output_language,
435 'GRITDIR': direction,
436 })
437 from grit.format import rc # avoid circular dep
438 rc.RcSubstitutions(self.substituter, self.output_language)
439 return self.substituter
440
441 def AssignFirstIds(self, filename_or_stream, defines):
442 """Assign first ids to each grouping node based on values from the
443 first_ids file (if specified on the <grit> node).
444 """
445 # If the input is a stream, then we're probably in a unit test and
446 # should skip this step.
447 if type(filename_or_stream) not in (str, unicode):
448 return
449
450 # Nothing to do if the first_ids_filename attribute isn't set.
451 first_ids_filename = self.GetFirstIdsFile()
452 if not first_ids_filename:
453 return
454
455 src_root_dir, first_ids = _ReadFirstIdsFromFile(first_ids_filename,
456 defines)
457 from grit.node import empty
458 for node in self.Preorder():
459 if isinstance(node, empty.GroupingNode):
460 abs_filename = os.path.abspath(filename_or_stream)
461 if abs_filename[:len(src_root_dir)] != src_root_dir:
462 filename = os.path.basename(filename_or_stream)
463 else:
464 filename = abs_filename[len(src_root_dir) + 1:]
465 filename = filename.replace('\\', '/')
466
467 if node.attrs['first_id'] != '':
468 raise Exception(
469 "Don't set the first_id attribute when using the first_ids_file "
470 "attribute on the <grit> node, update %s instead." %
471 first_ids_filename)
472
473 try:
474 id_list = first_ids[filename][node.name]
475 except KeyError, e:
476 print '-' * 78
477 print 'Resource id not set for %s (%s)!' % (filename, node.name)
478 print ('Please update %s to include an entry for %s. See the '
479 'comments in resource_ids for information on why you need to '
480 'update that file.' % (first_ids_filename, filename))
481 print '-' * 78
482 raise e
483
484 try:
485 node.attrs['first_id'] = str(id_list.pop(0))
486 except IndexError, e:
487 raise Exception('Please update %s and add a first id for %s (%s).'
488 % (first_ids_filename, filename, node.name))
489
490 def RunGatherers(self, debug=False):
491 '''Call RunPreSubstitutionGatherer() on every node of the tree, then apply
492 substitutions, then call RunPostSubstitutionGatherer() on every node.
493
494 The substitutions step requires that the output language has been set.
495 Locally, get the Substitution messages and add them to the substituter.
496 Also add substitutions for language codes in the Rc.
497
498 Args:
499 debug: will print information while running gatherers.
500 '''
501 for node in self.ActiveDescendants():
502 if hasattr(node, 'RunPreSubstitutionGatherer'):
503 with node:
504 node.RunPreSubstitutionGatherer(debug=debug)
505
506 assert self.output_language
507 self.SubstituteMessages(self.GetSubstituter())
508
509 for node in self.ActiveDescendants():
510 if hasattr(node, 'RunPostSubstitutionGatherer'):
511 with node:
512 node.RunPostSubstitutionGatherer(debug=debug)
513
514
515 class IdentifierNode(base.Node):
516 """A node for specifying identifiers that should appear in the resource
517 header file, and be unique amongst all other resource identifiers, but don't
518 have any other attributes or reference any resources.
519 """
520
521 def MandatoryAttributes(self):
522 return ['name']
523
524 def DefaultAttributes(self):
525 return { 'comment' : '', 'id' : '', 'systemid': 'false' }
526
527 def GetId(self):
528 """Returns the id of this identifier if it has one, None otherwise
529 """
530 if 'id' in self.attrs:
531 return self.attrs['id']
532 return None
533
534 def EndParsing(self):
535 """Handles system identifiers."""
536 super(IdentifierNode, self).EndParsing()
537 if self.attrs['systemid'] == 'true':
538 util.SetupSystemIdentifiers((self.attrs['name'],))
539
540 @staticmethod
541 def Construct(parent, name, id, comment, systemid='false'):
542 """Creates a new node which is a child of 'parent', with attributes set
543 by parameters of the same name.
544 """
545 node = IdentifierNode()
546 node.StartParsing('identifier', parent)
547 node.HandleAttribute('name', name)
548 node.HandleAttribute('id', id)
549 node.HandleAttribute('comment', comment)
550 node.HandleAttribute('systemid', systemid)
551 node.EndParsing()
552 return node
OLDNEW
« 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