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

Side by Side Diff: grit/node/structure.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/misc_unittest.py ('k') | grit/node/structure_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 '''The <structure> element.
7 '''
8
9 import os
10 import platform
11 import re
12
13 from grit import exception
14 from grit import util
15 from grit.node import base
16 from grit.node import variant
17
18 import grit.gather.admin_template
19 import grit.gather.chrome_html
20 import grit.gather.chrome_scaled_image
21 import grit.gather.igoogle_strings
22 import grit.gather.muppet_strings
23 import grit.gather.policy_json
24 import grit.gather.rc
25 import grit.gather.tr_html
26 import grit.gather.txt
27
28 import grit.format.rc
29 import grit.format.rc_header
30
31 # Type of the gatherer to use for each type attribute
32 _GATHERERS = {
33 'accelerators' : grit.gather.rc.Accelerators,
34 'admin_template' : grit.gather.admin_template.AdmGatherer,
35 'chrome_html' : grit.gather.chrome_html.ChromeHtml,
36 'chrome_scaled_image' : grit.gather.chrome_scaled_image.ChromeScaledImage,
37 'dialog' : grit.gather.rc.Dialog,
38 'igoogle' : grit.gather.igoogle_strings.IgoogleStrings,
39 'menu' : grit.gather.rc.Menu,
40 'muppet' : grit.gather.muppet_strings.MuppetStrings,
41 'rcdata' : grit.gather.rc.RCData,
42 'tr_html' : grit.gather.tr_html.TrHtml,
43 'txt' : grit.gather.txt.TxtFile,
44 'version' : grit.gather.rc.Version,
45 'policy_template_metafile' : grit.gather.policy_json.PolicyJson,
46 }
47
48
49 # TODO(joi) Print a warning if the 'variant_of_revision' attribute indicates
50 # that a skeleton variant is older than the original file.
51
52
53 class StructureNode(base.Node):
54 '''A <structure> element.'''
55
56 # Regular expression for a local variable definition. Each definition
57 # is of the form NAME=VALUE, where NAME cannot contain '=' or ',' and
58 # VALUE must escape all commas: ',' -> ',,'. Each variable definition
59 # should be separated by a comma with no extra whitespace.
60 # Example: THING1=foo,THING2=bar
61 variable_pattern = re.compile('([^,=\s]+)=((?:,,|[^,])*)')
62
63 def __init__(self):
64 super(StructureNode, self).__init__()
65
66 # Keep track of the last filename we flattened to, so we can
67 # avoid doing it more than once.
68 self._last_flat_filename = None
69
70 # See _Substitute; this substituter is used for local variables and
71 # the root substituter is used for global variables.
72 self.substituter = None
73
74 def _IsValidChild(self, child):
75 return isinstance(child, variant.SkeletonNode)
76
77 def _ParseVariables(self, variables):
78 '''Parse a variable string into a dictionary.'''
79 matches = StructureNode.variable_pattern.findall(variables)
80 return dict((name, value.replace(',,', ',')) for name, value in matches)
81
82 def EndParsing(self):
83 super(StructureNode, self).EndParsing()
84
85 # Now that we have attributes and children, instantiate the gatherers.
86 gathertype = _GATHERERS[self.attrs['type']]
87
88 self.gatherer = gathertype(self.attrs['file'],
89 self.attrs['name'],
90 self.attrs['encoding'])
91 self.gatherer.SetGrdNode(self)
92 self.gatherer.SetUberClique(self.UberClique())
93 if hasattr(self.GetRoot(), 'defines'):
94 self.gatherer.SetDefines(self.GetRoot().defines)
95 self.gatherer.SetAttributes(self.attrs)
96 if self.ExpandVariables():
97 self.gatherer.SetFilenameExpansionFunction(self._Substitute)
98
99 # Parse local variables and instantiate the substituter.
100 if self.attrs['variables']:
101 variables = self.attrs['variables']
102 self.substituter = util.Substituter()
103 self.substituter.AddSubstitutions(self._ParseVariables(variables))
104
105 self.skeletons = {} # Maps expressions to skeleton gatherers
106 for child in self.children:
107 assert isinstance(child, variant.SkeletonNode)
108 skel = gathertype(child.attrs['file'],
109 self.attrs['name'],
110 child.GetEncodingToUse(),
111 is_skeleton=True)
112 skel.SetGrdNode(self) # TODO(benrg): Or child? Only used for ToRealPath
113 skel.SetUberClique(self.UberClique())
114 if hasattr(self.GetRoot(), 'defines'):
115 skel.SetDefines(self.GetRoot().defines)
116 if self.ExpandVariables():
117 skel.SetFilenameExpansionFunction(self._Substitute)
118 self.skeletons[child.attrs['expr']] = skel
119
120 def MandatoryAttributes(self):
121 return ['type', 'name', 'file']
122
123 def DefaultAttributes(self):
124 return { 'encoding' : 'cp1252',
125 'exclude_from_rc' : 'false',
126 'line_end' : 'unix',
127 'output_encoding' : 'utf-8',
128 'generateid': 'true',
129 'expand_variables' : 'false',
130 'output_filename' : '',
131 'fold_whitespace': 'false',
132 # Run an arbitrary command after translation is complete
133 # so that it doesn't interfere with what's in translation
134 # console.
135 'run_command' : '',
136 # Leave empty to run on all platforms, comma-separated
137 # for one or more specific platforms. Values must match
138 # output of platform.system().
139 'run_command_on_platforms' : '',
140 'allowexternalscript': 'false',
141 'flattenhtml': 'false',
142 'fallback_to_low_resolution': 'default',
143 # TODO(joi) this is a hack - should output all generated files
144 # as SCons dependencies; however, for now there is a bug I can't
145 # find where GRIT doesn't build the matching fileset, therefore
146 # this hack so that only the files you really need are marked as
147 # dependencies.
148 'sconsdep' : 'false',
149 'variables': '',
150 }
151
152 def IsExcludedFromRc(self):
153 return self.attrs['exclude_from_rc'] == 'true'
154
155 def Process(self, output_dir):
156 """Writes the processed data to output_dir. In the case of a chrome_html
157 structure this will add references to other scale factors. If flattening
158 this will also write file references to be base64 encoded data URLs. The
159 name of the new file is returned."""
160 filename = self.ToRealPath(self.GetInputPath())
161 flat_filename = os.path.join(output_dir,
162 self.attrs['name'] + '_' + os.path.basename(filename))
163
164 if self._last_flat_filename == flat_filename:
165 return
166
167 with open(flat_filename, 'wb') as outfile:
168 if self.ExpandVariables():
169 text = self.gatherer.GetText()
170 file_contents = self._Substitute(text).encode('utf-8')
171 else:
172 file_contents = self.gatherer.GetData('', 'utf-8')
173 outfile.write(file_contents)
174
175 self._last_flat_filename = flat_filename
176 return os.path.basename(flat_filename)
177
178 def GetLineEnd(self):
179 '''Returns the end-of-line character or characters for files output because
180 of this node ('\r\n', '\n', or '\r' depending on the 'line_end' attribute).
181 '''
182 if self.attrs['line_end'] == 'unix':
183 return '\n'
184 elif self.attrs['line_end'] == 'windows':
185 return '\r\n'
186 elif self.attrs['line_end'] == 'mac':
187 return '\r'
188 else:
189 raise exception.UnexpectedAttribute(
190 "Attribute 'line_end' must be one of 'unix' (default), 'windows' or 'mac '")
191
192 def GetCliques(self):
193 return self.gatherer.GetCliques()
194
195 def GetDataPackPair(self, lang, encoding):
196 """Returns a (id, string|None) pair that represents the resource id and raw
197 bytes of the data (or None if no resource is generated). This is used to
198 generate the data pack data file.
199 """
200 from grit.format import rc_header
201 id_map = rc_header.GetIds(self.GetRoot())
202 id = id_map[self.GetTextualIds()[0]]
203 if self.ExpandVariables():
204 text = self.gatherer.GetText()
205 return id, util.Encode(self._Substitute(text), encoding)
206 return id, self.gatherer.GetData(lang, encoding)
207
208 def GetHtmlResourceFilenames(self):
209 """Returns a set of all filenames inlined by this node."""
210 return self.gatherer.GetHtmlResourceFilenames()
211
212 def GetInputPath(self):
213 return self.gatherer.GetInputPath()
214
215 def GetTextualIds(self):
216 if not hasattr(self, 'gatherer'):
217 # This case is needed because this method is called by
218 # GritNode.ValidateUniqueIds before RunGatherers has been called.
219 # TODO(benrg): Fix this?
220 return [self.attrs['name']]
221 return self.gatherer.GetTextualIds()
222
223 def RunPreSubstitutionGatherer(self, debug=False):
224 if debug:
225 print 'Running gatherer %s for file %s' % (
226 str(type(self.gatherer)), self.GetInputPath())
227
228 # Note: Parse() is idempotent, therefore this method is also.
229 self.gatherer.Parse()
230 for skel in self.skeletons.values():
231 skel.Parse()
232
233 def GetSkeletonGatherer(self):
234 '''Returns the gatherer for the alternate skeleton that should be used,
235 based on the expressions for selecting skeletons, or None if the skeleton
236 from the English version of the structure should be used.
237 '''
238 for expr in self.skeletons:
239 if self.EvaluateCondition(expr):
240 return self.skeletons[expr]
241 return None
242
243 def HasFileForLanguage(self):
244 return self.attrs['type'] in ['tr_html', 'admin_template', 'txt',
245 'muppet', 'igoogle', 'chrome_scaled_image',
246 'chrome_html']
247
248 def ExpandVariables(self):
249 '''Variable expansion on structures is controlled by an XML attribute.
250
251 However, old files assume that expansion is always on for Rc files.
252
253 Returns:
254 A boolean.
255 '''
256 attrs = self.GetRoot().attrs
257 if 'grit_version' in attrs and attrs['grit_version'] > 1:
258 return self.attrs['expand_variables'] == 'true'
259 else:
260 return (self.attrs['expand_variables'] == 'true' or
261 self.attrs['file'].lower().endswith('.rc'))
262
263 def _Substitute(self, text):
264 '''Perform local and global variable substitution.'''
265 if self.substituter:
266 text = self.substituter.Substitute(text)
267 return self.GetRoot().GetSubstituter().Substitute(text)
268
269 def RunCommandOnCurrentPlatform(self):
270 if self.attrs['run_command_on_platforms'] == '':
271 return True
272 else:
273 target_platforms = self.attrs['run_command_on_platforms'].split(',')
274 return platform.system() in target_platforms
275
276 def FileForLanguage(self, lang, output_dir, create_file=True,
277 return_if_not_generated=True):
278 '''Returns the filename of the file associated with this structure,
279 for the specified language.
280
281 Args:
282 lang: 'fr'
283 output_dir: 'c:\temp'
284 create_file: True
285 '''
286 assert self.HasFileForLanguage()
287 # If the source language is requested, and no extra changes are requested,
288 # use the existing file.
289 if ((not lang or lang == self.GetRoot().GetSourceLanguage()) and
290 self.attrs['expand_variables'] != 'true' and
291 (not self.attrs['run_command'] or
292 not self.RunCommandOnCurrentPlatform())):
293 if return_if_not_generated:
294 input_path = self.GetInputPath()
295 if input_path is None:
296 return None
297 return self.ToRealPath(input_path)
298 else:
299 return None
300
301 if self.attrs['output_filename'] != '':
302 filename = self.attrs['output_filename']
303 else:
304 filename = os.path.basename(self.attrs['file'])
305 assert len(filename)
306 filename = '%s_%s' % (lang, filename)
307 filename = os.path.join(output_dir, filename)
308
309 # Only create the output if it was requested by the call.
310 if create_file:
311 text = self.gatherer.Translate(
312 lang,
313 pseudo_if_not_available=self.PseudoIsAllowed(),
314 fallback_to_english=self.ShouldFallbackToEnglish(),
315 skeleton_gatherer=self.GetSkeletonGatherer())
316
317 file_contents = util.FixLineEnd(text, self.GetLineEnd())
318 if self.ExpandVariables():
319 # Note that we reapply substitution a second time here.
320 # This is because a) we need to look inside placeholders
321 # b) the substitution values are language-dependent
322 file_contents = self._Substitute(file_contents)
323
324 with open(filename, 'wb') as file_object:
325 output_stream = util.WrapOutputStream(file_object,
326 self.attrs['output_encoding'])
327 output_stream.write(file_contents)
328
329 if self.attrs['run_command'] and self.RunCommandOnCurrentPlatform():
330 # Run arbitrary commands after translation is complete so that it
331 # doesn't interfere with what's in translation console.
332 command = self.attrs['run_command'] % {'filename': filename}
333 result = os.system(command)
334 assert result == 0, '"%s" failed.' % command
335
336 return filename
337
338 def IsResourceMapSource(self):
339 return True
340
341 def GeneratesResourceMapEntry(self, output_all_resource_defines,
342 is_active_descendant):
343 if output_all_resource_defines:
344 return True
345 return is_active_descendant
346
347 @staticmethod
348 def Construct(parent, name, type, file, encoding='cp1252'):
349 '''Creates a new node which is a child of 'parent', with attributes set
350 by parameters of the same name.
351 '''
352 node = StructureNode()
353 node.StartParsing('structure', parent)
354 node.HandleAttribute('name', name)
355 node.HandleAttribute('type', type)
356 node.HandleAttribute('file', file)
357 node.HandleAttribute('encoding', encoding)
358 node.EndParsing()
359 return node
360
361 def SubstituteMessages(self, substituter):
362 '''Propagates substitution to gatherer.
363
364 Args:
365 substituter: a grit.util.Substituter object.
366 '''
367 assert hasattr(self, 'gatherer')
368 if self.ExpandVariables():
369 self.gatherer.SubstituteMessages(substituter)
370
OLDNEW
« no previous file with comments | « grit/node/misc_unittest.py ('k') | grit/node/structure_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698