PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/tools/grit/grit/node/message.py

https://github.com/Gitman1989/chromium
Python | 276 lines | 230 code | 20 blank | 26 comment | 8 complexity | 766c0d935282fee380a75137786ce413 MD5 | raw file
  1. #!/usr/bin/python2.4
  2. # Copyright (c) 2006-2008 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. '''Handling of the <message> element.
  6. '''
  7. import re
  8. import types
  9. from grit.node import base
  10. import grit.format.rc_header
  11. import grit.format.rc
  12. from grit import clique
  13. from grit import exception
  14. from grit import tclib
  15. from grit import util
  16. # Finds whitespace at the start and end of a string which can be multiline.
  17. _WHITESPACE = re.compile('(?P<start>\s*)(?P<body>.+?)(?P<end>\s*)\Z',
  18. re.DOTALL | re.MULTILINE)
  19. class MessageNode(base.ContentNode):
  20. '''A <message> element.'''
  21. # For splitting a list of things that can be separated by commas or
  22. # whitespace
  23. _SPLIT_RE = re.compile('\s*,\s*|\s+')
  24. def __init__(self):
  25. super(type(self), self).__init__()
  26. # Valid after EndParsing, this is the MessageClique that contains the
  27. # source message and any translations of it that have been loaded.
  28. self.clique = None
  29. # We don't send leading and trailing whitespace into the translation
  30. # console, but rather tack it onto the source message and any
  31. # translations when formatting them into RC files or what have you.
  32. self.ws_at_start = '' # Any whitespace characters at the start of the text
  33. self.ws_at_end = '' # --"-- at the end of the text
  34. # A list of "shortcut groups" this message is in. We check to make sure
  35. # that shortcut keys (e.g. &J) within each shortcut group are unique.
  36. self.shortcut_groups_ = []
  37. def _IsValidChild(self, child):
  38. return isinstance(child, (PhNode))
  39. def _IsValidAttribute(self, name, value):
  40. if name not in ['name', 'offset', 'translateable', 'desc', 'meaning',
  41. 'internal_comment', 'shortcut_groups', 'custom_type',
  42. 'validation_expr', 'use_name_for_id']:
  43. return False
  44. if name == 'translateable' and value not in ['true', 'false']:
  45. return False
  46. return True
  47. def MandatoryAttributes(self):
  48. return ['name|offset']
  49. def DefaultAttributes(self):
  50. return {
  51. 'translateable' : 'true',
  52. 'desc' : '',
  53. 'meaning' : '',
  54. 'internal_comment' : '',
  55. 'shortcut_groups' : '',
  56. 'custom_type' : '',
  57. 'validation_expr' : '',
  58. 'use_name_for_id' : 'false',
  59. }
  60. def GetTextualIds(self):
  61. '''
  62. Returns the concatenation of the parent's node first_id and
  63. this node's offset if it has one, otherwise just call the
  64. superclass' implementation
  65. '''
  66. if 'offset' in self.attrs:
  67. # we search for the first grouping node in the parents' list
  68. # to take care of the case where the first parent is an <if> node
  69. grouping_parent = self.parent
  70. import grit.node.empty
  71. while grouping_parent and not isinstance(grouping_parent,
  72. grit.node.empty.GroupingNode):
  73. grouping_parent = grouping_parent.parent
  74. assert 'first_id' in grouping_parent.attrs
  75. return [grouping_parent.attrs['first_id'] + '_' + self.attrs['offset']]
  76. else:
  77. return super(type(self), self).GetTextualIds()
  78. def IsTranslateable(self):
  79. return self.attrs['translateable'] == 'true'
  80. def ItemFormatter(self, t):
  81. # Only generate an output if the if condition is satisfied.
  82. if not self.SatisfiesOutputCondition():
  83. return super(type(self), self).ItemFormatter(t)
  84. if t == 'rc_header':
  85. return grit.format.rc_header.Item()
  86. elif t in ('rc_all', 'rc_translateable', 'rc_nontranslateable'):
  87. return grit.format.rc.Message()
  88. elif t == 'js_map_format':
  89. return grit.format.js_map_format.Message()
  90. else:
  91. return super(type(self), self).ItemFormatter(t)
  92. def EndParsing(self):
  93. super(type(self), self).EndParsing()
  94. # Make the text (including placeholder references) and list of placeholders,
  95. # then strip and store leading and trailing whitespace and create the
  96. # tclib.Message() and a clique to contain it.
  97. text = ''
  98. placeholders = []
  99. for item in self.mixed_content:
  100. if isinstance(item, types.StringTypes):
  101. text += item
  102. else:
  103. presentation = item.attrs['name'].upper()
  104. text += presentation
  105. ex = ' '
  106. if len(item.children):
  107. ex = item.children[0].GetCdata()
  108. original = item.GetCdata()
  109. placeholders.append(tclib.Placeholder(presentation, original, ex))
  110. m = _WHITESPACE.match(text)
  111. if m:
  112. self.ws_at_start = m.group('start')
  113. self.ws_at_end = m.group('end')
  114. text = m.group('body')
  115. self.shortcut_groups_ = self._SPLIT_RE.split(self.attrs['shortcut_groups'])
  116. self.shortcut_groups_ = [i for i in self.shortcut_groups_ if i != '']
  117. description_or_id = self.attrs['desc']
  118. if description_or_id == '' and 'name' in self.attrs:
  119. description_or_id = 'ID: %s' % self.attrs['name']
  120. assigned_id = None
  121. if (self.attrs['use_name_for_id'] == 'true' and
  122. self.SatisfiesOutputCondition()):
  123. assigned_id = self.attrs['name']
  124. message = tclib.Message(text=text, placeholders=placeholders,
  125. description=description_or_id,
  126. meaning=self.attrs['meaning'],
  127. assigned_id=assigned_id)
  128. self.clique = self.UberClique().MakeClique(message, self.IsTranslateable())
  129. for group in self.shortcut_groups_:
  130. self.clique.AddToShortcutGroup(group)
  131. if self.attrs['custom_type'] != '':
  132. self.clique.SetCustomType(util.NewClassInstance(self.attrs['custom_type'],
  133. clique.CustomType))
  134. elif self.attrs['validation_expr'] != '':
  135. self.clique.SetCustomType(
  136. clique.OneOffCustomType(self.attrs['validation_expr']))
  137. def GetCliques(self):
  138. if self.clique:
  139. return [self.clique]
  140. else:
  141. return []
  142. def Translate(self, lang):
  143. '''Returns a translated version of this message.
  144. '''
  145. assert self.clique
  146. msg = self.clique.MessageForLanguage(lang,
  147. self.PseudoIsAllowed(),
  148. self.ShouldFallbackToEnglish()
  149. ).GetRealContent()
  150. return msg.replace('[GRITLANGCODE]', lang)
  151. def NameOrOffset(self):
  152. if 'name' in self.attrs:
  153. return self.attrs['name']
  154. else:
  155. return self.attrs['offset']
  156. def GetDataPackPair(self, output_dir, lang):
  157. '''Returns a (id, string) pair that represents the string id and the string
  158. in utf8. This is used to generate the data pack data file.
  159. '''
  160. from grit.format import rc_header
  161. id_map = rc_header.Item.tids_
  162. id = id_map[self.GetTextualIds()[0]]
  163. message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
  164. if "\\n" in message:
  165. # Windows automatically translates \n to a new line, but GTK+ doesn't.
  166. # Manually do the conversion here rather than at run time.
  167. message = message.replace("\\n", "\n")
  168. # |message| is a python unicode string, so convert to a utf16 byte stream
  169. # because that's the format of datapacks. We skip the first 2 bytes
  170. # because it is the BOM.
  171. return id, message.encode('utf16')[2:]
  172. # static method
  173. def Construct(parent, message, name, desc='', meaning='', translateable=True):
  174. '''Constructs a new message node that is a child of 'parent', with the
  175. name, desc, meaning and translateable attributes set using the same-named
  176. parameters and the text of the message and any placeholders taken from
  177. 'message', which must be a tclib.Message() object.'''
  178. # Convert type to appropriate string
  179. if translateable:
  180. translateable = 'true'
  181. else:
  182. translateable = 'false'
  183. node = MessageNode()
  184. node.StartParsing('message', parent)
  185. node.HandleAttribute('name', name)
  186. node.HandleAttribute('desc', desc)
  187. node.HandleAttribute('meaning', meaning)
  188. node.HandleAttribute('translateable', translateable)
  189. items = message.GetContent()
  190. for ix in range(len(items)):
  191. if isinstance(items[ix], types.StringTypes):
  192. text = items[ix]
  193. # Ensure whitespace at front and back of message is correctly handled.
  194. if ix == 0:
  195. text = "'''" + text
  196. if ix == len(items) - 1:
  197. text = text + "'''"
  198. node.AppendContent(text)
  199. else:
  200. phnode = PhNode()
  201. phnode.StartParsing('ph', node)
  202. phnode.HandleAttribute('name', items[ix].GetPresentation())
  203. phnode.AppendContent(items[ix].GetOriginal())
  204. if len(items[ix].GetExample()) and items[ix].GetExample() != ' ':
  205. exnode = ExNode()
  206. exnode.StartParsing('ex', phnode)
  207. exnode.AppendContent(items[ix].GetExample())
  208. exnode.EndParsing()
  209. phnode.AddChild(exnode)
  210. phnode.EndParsing()
  211. node.AddChild(phnode)
  212. node.EndParsing()
  213. return node
  214. Construct = staticmethod(Construct)
  215. class PhNode(base.ContentNode):
  216. '''A <ph> element.'''
  217. def _IsValidChild(self, child):
  218. return isinstance(child, ExNode)
  219. def MandatoryAttributes(self):
  220. return ['name']
  221. def EndParsing(self):
  222. super(type(self), self).EndParsing()
  223. # We only allow a single example for each placeholder
  224. if len(self.children) > 1:
  225. raise exception.TooManyExamples()
  226. class ExNode(base.ContentNode):
  227. '''An <ex> element.'''
  228. pass