195640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org#!/usr/bin/env python
295640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org# Copyright (c) 2012 The Chromium Authors. All rights reserved.
301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Use of this source code is governed by a BSD-style license that can be
401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# found in the LICENSE file.
501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''Handling of the <message> element.
701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''
801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport re
1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport types
1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.node import base
1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport grit.format.rc_header
1501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport grit.format.rc
1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import clique
1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import exception
1901fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.orgfrom grit import lazy_re
2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import tclib
2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import util
2201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Finds whitespace at the start and end of a string which can be multiline.
2401fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org_WHITESPACE = lazy_re.compile('(?P<start>\s*)(?P<body>.+?)(?P<end>\s*)\Z',
2501fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org                              re.DOTALL | re.MULTILINE)
2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass MessageNode(base.ContentNode):
2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''A <message> element.'''
3001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
3101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # For splitting a list of things that can be separated by commas or
3201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # whitespace
3301fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org  _SPLIT_RE = lazy_re.compile('\s*,\s*|\s+')
3401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
3501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __init__(self):
36b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    super(MessageNode, self).__init__()
3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Valid after EndParsing, this is the MessageClique that contains the
3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # source message and any translations of it that have been loaded.
3901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.clique = None
4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # We don't send leading and trailing whitespace into the translation
4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # console, but rather tack it onto the source message and any
4301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # translations when formatting them into RC files or what have you.
4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.ws_at_start = ''  # Any whitespace characters at the start of the text
4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.ws_at_end = ''  # --"-- at the end of the text
4601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # A list of "shortcut groups" this message is in.  We check to make sure
4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # that shortcut keys (e.g. &J) within each shortcut group are unique.
4901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.shortcut_groups_ = []
5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
51f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org    # Formatter-specific data used to control the output of individual strings.
52f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org    # formatter_data is a space separated list of C preprocessor-style
53f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org    # definitions. Names without values are given the empty string value.
54f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org    # Example: "foo=5 bar baz=100"
55f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org    self.formatter_data = {}
56f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org
5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _IsValidChild(self, child):
5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return isinstance(child, (PhNode))
5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _IsValidAttribute(self, name, value):
6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if name not in ['name', 'offset', 'translateable', 'desc', 'meaning',
6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                    'internal_comment', 'shortcut_groups', 'custom_type',
63f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org                    'validation_expr', 'use_name_for_id', 'sub_variable',
64f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org                    'formatter_data']:
6501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return False
6677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    if (name in ('translateable', 'sub_variable') and
6777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org        value not in ['true', 'false']):
6801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return False
6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return True
7001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def MandatoryAttributes(self):
7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return ['name|offset']
7301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def DefaultAttributes(self):
7501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return {
7677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      'custom_type' : '',
7701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      'desc' : '',
78f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org      'formatter_data' : '',
7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      'internal_comment' : '',
8077cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      'meaning' : '',
8101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      'shortcut_groups' : '',
8277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      'sub_variable' : 'false',
8377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      'translateable' : 'true',
8401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      'use_name_for_id' : 'false',
8577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      'validation_expr' : '',
8601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    }
8701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
88f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org  def HandleAttribute(self, attrib, value):
89f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org    base.ContentNode.HandleAttribute(self, attrib, value)
90f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org    if attrib == 'formatter_data':
91f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org      # Parse value, a space-separated list of defines, into a dict.
92f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org      # Example: "foo=5 bar" -> {'foo':'5', 'bar':''}
93f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org      for item in value.split():
94f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org        name, sep, val = item.partition('=')
95f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org        self.formatter_data[name] = val
96f221c01d847d97c0a487f4059ee33d4d7b6d7d5fnewt@chromium.org
9701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetTextualIds(self):
9801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''
9901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Returns the concatenation of the parent's node first_id and
10001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    this node's offset if it has one, otherwise just call the
10101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    superclass' implementation
10201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''
10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if 'offset' in self.attrs:
10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # we search for the first grouping node in the parents' list
10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # to take care of the case where the first parent is an <if> node
10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      grouping_parent = self.parent
10701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      import grit.node.empty
10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      while grouping_parent and not isinstance(grouping_parent,
10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                               grit.node.empty.GroupingNode):
11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        grouping_parent = grouping_parent.parent
11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      assert 'first_id' in grouping_parent.attrs
11301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return [grouping_parent.attrs['first_id'] + '_' + self.attrs['offset']]
11401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
115b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org      return super(MessageNode, self).GetTextualIds()
11601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
11701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def IsTranslateable(self):
11801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.attrs['translateable'] == 'true'
11901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
12001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def EndParsing(self):
121b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    super(MessageNode, self).EndParsing()
12201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
12301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Make the text (including placeholder references) and list of placeholders,
12401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # then strip and store leading and trailing whitespace and create the
12501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # tclib.Message() and a clique to contain it.
12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
12701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    text = ''
12801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    placeholders = []
12901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for item in self.mixed_content:
13001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if isinstance(item, types.StringTypes):
13101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        text += item
13201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      else:
13301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        presentation = item.attrs['name'].upper()
13401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        text += presentation
13501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        ex = ' '
13601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        if len(item.children):
13701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          ex = item.children[0].GetCdata()
13801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        original = item.GetCdata()
13901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        placeholders.append(tclib.Placeholder(presentation, original, ex))
14001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
14101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    m = _WHITESPACE.match(text)
14201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if m:
14301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.ws_at_start = m.group('start')
14401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.ws_at_end = m.group('end')
14501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      text = m.group('body')
14601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
14701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.shortcut_groups_ = self._SPLIT_RE.split(self.attrs['shortcut_groups'])
14801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.shortcut_groups_ = [i for i in self.shortcut_groups_ if i != '']
14901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    description_or_id = self.attrs['desc']
15101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if description_or_id == '' and 'name' in self.attrs:
15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      description_or_id = 'ID: %s' % self.attrs['name']
15301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    assigned_id = None
155ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    if self.attrs['use_name_for_id'] == 'true':
15601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      assigned_id = self.attrs['name']
15701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    message = tclib.Message(text=text, placeholders=placeholders,
15801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                            description=description_or_id,
15901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                            meaning=self.attrs['meaning'],
16001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                            assigned_id=assigned_id)
16177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    self.InstallMessage(message)
16277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
16377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org  def InstallMessage(self, message):
16477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    '''Sets this node's clique from a tclib.Message instance.
16577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
16677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    Args:
16777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      message: A tclib.Message.
16877cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    '''
16901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.clique = self.UberClique().MakeClique(message, self.IsTranslateable())
17001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for group in self.shortcut_groups_:
17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.clique.AddToShortcutGroup(group)
17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.attrs['custom_type'] != '':
17301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.clique.SetCustomType(util.NewClassInstance(self.attrs['custom_type'],
17401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                                      clique.CustomType))
17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    elif self.attrs['validation_expr'] != '':
17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.clique.SetCustomType(
17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        clique.OneOffCustomType(self.attrs['validation_expr']))
17801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17977cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org  def SubstituteMessages(self, substituter):
18077cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    '''Applies substitution to this message.
18177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
18277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    Args:
18377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      substituter: a grit.util.Substituter object.
18477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    '''
18577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    message = substituter.SubstituteMessage(self.clique.GetMessage())
18677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    if message is not self.clique.GetMessage():
18777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      self.InstallMessage(message)
18877cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetCliques(self):
19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.clique:
19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return [self.clique]
19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return []
19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
19501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Translate(self, lang):
19601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Returns a translated version of this message.
19701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''
19801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    assert self.clique
19901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    msg = self.clique.MessageForLanguage(lang,
20001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                         self.PseudoIsAllowed(),
20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                         self.ShouldFallbackToEnglish()
20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                         ).GetRealContent()
20301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return msg.replace('[GRITLANGCODE]', lang)
20401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
20501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def NameOrOffset(self):
20601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if 'name' in self.attrs:
20701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return self.attrs['name']
20801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
20901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return self.attrs['offset']
21001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org  def ExpandVariables(self):
21277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    '''We always expand variables on Messages.'''
21377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    return True
21477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetDataPackPair(self, lang, encoding):
21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Returns a (id, string) pair that represents the string id and the string
2174460b853d6df39e3b89ae1abb278e9241db72e02joaodasilva@chromium.org    in the specified encoding, where |encoding| is one of the encoding values
2184460b853d6df39e3b89ae1abb278e9241db72e02joaodasilva@chromium.org    accepted by util.Encode.  This is used to generate the data pack data file.
21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''
22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    from grit.format import rc_header
221ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    id_map = rc_header.GetIds(self.GetRoot())
22201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    id = id_map[self.GetTextualIds()[0]]
22301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
22401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
2254460b853d6df39e3b89ae1abb278e9241db72e02joaodasilva@chromium.org    return id, util.Encode(message, encoding)
22601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
227b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org  @staticmethod
22801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Construct(parent, message, name, desc='', meaning='', translateable=True):
22901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Constructs a new message node that is a child of 'parent', with the
23001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    name, desc, meaning and translateable attributes set using the same-named
23101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    parameters and the text of the message and any placeholders taken from
23201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    'message', which must be a tclib.Message() object.'''
23301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Convert type to appropriate string
234b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    translateable = 'true' if translateable else 'false'
23501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
23601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node = MessageNode()
23701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.StartParsing('message', parent)
23801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.HandleAttribute('name', name)
23901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.HandleAttribute('desc', desc)
24001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.HandleAttribute('meaning', meaning)
24101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.HandleAttribute('translateable', translateable)
24201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
24301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    items = message.GetContent()
244b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    for ix, item in enumerate(items):
245b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org      if isinstance(item, types.StringTypes):
24601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        # Ensure whitespace at front and back of message is correctly handled.
24701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        if ix == 0:
248b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org          item = "'''" + item
24901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        if ix == len(items) - 1:
250b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org          item = item + "'''"
25101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
252b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org        node.AppendContent(item)
25301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      else:
25401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        phnode = PhNode()
25501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        phnode.StartParsing('ph', node)
256b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org        phnode.HandleAttribute('name', item.GetPresentation())
257b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org        phnode.AppendContent(item.GetOriginal())
25801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
259b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org        if len(item.GetExample()) and item.GetExample() != ' ':
26001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          exnode = ExNode()
26101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          exnode.StartParsing('ex', phnode)
262b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org          exnode.AppendContent(item.GetExample())
26301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          exnode.EndParsing()
26401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          phnode.AddChild(exnode)
26501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        phnode.EndParsing()
26701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        node.AddChild(phnode)
26801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.EndParsing()
27001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return node
27101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass PhNode(base.ContentNode):
27301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''A <ph> element.'''
27401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _IsValidChild(self, child):
27601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return isinstance(child, ExNode)
27701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def MandatoryAttributes(self):
27901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return ['name']
28001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
28101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def EndParsing(self):
282b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    super(PhNode, self).EndParsing()
28301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # We only allow a single example for each placeholder
28401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(self.children) > 1:
28501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      raise exception.TooManyExamples()
28601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
287ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  def GetTextualIds(self):
288ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    # The 'name' attribute is not an ID.
289ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    return []
290ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org
29101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
29201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass ExNode(base.ContentNode):
29301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''An <ex> element.'''
29401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  pass
295