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
6eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org"""Miscellaneous node types.
7eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org"""
801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport os.path
1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport re
11abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.orgimport sys
1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import constants
14b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.orgfrom grit import exception
1501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import util
1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport grit.format.rc_header
17eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.orgfrom grit.node import base
18eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.orgfrom grit.node import io
19b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.orgfrom grit.node import message
2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org# RTL languages
2377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org# TODO(jennyz): remove this fixed set of RTL language array
2477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org# now that generic expand_variable code exists.
2577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org_RTL_LANGS = (
2677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    'ar',  # Arabic
2777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    'fa',  # Farsi
2877cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    'iw',  # Hebrew
2977cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    'ks',  # Kashmiri
3077cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    'ku',  # Kurdish
3177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    'ps',  # Pashto
3277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    'ur',  # Urdu
3377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    'yi',  # Yiddish
3477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org)
3577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
3677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgdef _ReadFirstIdsFromFile(filename, defines):
38eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org  """Read the starting resource id values from |filename|.  We also
3901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  expand variables of the form <(FOO) based on defines passed in on
4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  the command line.
4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  Returns a tuple, the absolute path of SRCDIR followed by the
4301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  first_ids dictionary.
44eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org  """
45b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org  first_ids_dict = eval(util.ReadFile(filename, util.RAW_TEXT))
4601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  src_root_dir = os.path.abspath(os.path.join(os.path.dirname(filename),
4701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                              first_ids_dict['SRCDIR']))
4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def ReplaceVariable(matchobj):
5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for key, value in defines.iteritems():
5101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if matchobj.group(1) == key:
5201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        return value
5301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return ''
5401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  renames = []
5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  for grd_filename in first_ids_dict:
5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    new_grd_filename = re.sub(r'<\(([A-Za-z_]+)\)', ReplaceVariable,
5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                              grd_filename)
5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if new_grd_filename != grd_filename:
606740fffb81bff917e8f43523e922baaed7929824joi@chromium.org      abs_grd_filename = os.path.abspath(new_grd_filename)
616740fffb81bff917e8f43523e922baaed7929824joi@chromium.org      if abs_grd_filename[:len(src_root_dir)] != src_root_dir:
626740fffb81bff917e8f43523e922baaed7929824joi@chromium.org        new_grd_filename = os.path.basename(abs_grd_filename)
636740fffb81bff917e8f43523e922baaed7929824joi@chromium.org      else:
646740fffb81bff917e8f43523e922baaed7929824joi@chromium.org        new_grd_filename = abs_grd_filename[len(src_root_dir) + 1:]
656740fffb81bff917e8f43523e922baaed7929824joi@chromium.org        new_grd_filename = new_grd_filename.replace('\\', '/')
6601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      renames.append((grd_filename, new_grd_filename))
6701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  for grd_filename, new_grd_filename in renames:
6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    first_ids_dict[new_grd_filename] = first_ids_dict[grd_filename]
7001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    del(first_ids_dict[grd_filename])
7101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  return (src_root_dir, first_ids_dict)
7301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.orgclass SplicingNode(base.Node):
7683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  """A node whose children should be considered to be at the same level as
7783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  its siblings for most purposes. This includes <if> and <part> nodes.
78eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org  """
7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
8001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _IsValidChild(self, child):
8183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    assert self.parent, '<%s> node should never be root.' % self.name
8283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if isinstance(child, SplicingNode):
8383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      return True  # avoid O(n^2) behavior
8483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    return self.parent._IsValidChild(child)
8583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
8683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
8783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.orgclass IfNode(SplicingNode):
8883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  """A node for conditional inclusion of resources.
8983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  """
9001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
9101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def MandatoryAttributes(self):
9201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return ['expr']
9301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
940b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org  def _IsValidChild(self, child):
950b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org    return (isinstance(child, (ThenNode, ElseNode)) or
960b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org            super(IfNode, self)._IsValidChild(child))
970b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org
980b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org  def EndParsing(self):
990b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org    children = self.children
1000b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org    self.if_then_else = False
1010b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org    if any(isinstance(node, (ThenNode, ElseNode)) for node in children):
1020b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org      if (len(children) != 2 or not isinstance(children[0], ThenNode) or
1030b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org                                not isinstance(children[1], ElseNode)):
1040b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org        raise exception.UnexpectedChild(
1050b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org            '<if> element must be <if><then>...</then><else>...</else></if>')
1060b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org      self.if_then_else = True
1070b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org
108ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  def ActiveChildren(self):
1090b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org    cond = self.EvaluateCondition(self.attrs['expr'])
1100b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org    if self.if_then_else:
1110b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org      return self.children[0 if cond else 1].ActiveChildren()
1120b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org    else:
1130b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org      # Equivalent to having all children inside <then> with an empty <else>
1140b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org      return super(IfNode, self).ActiveChildren() if cond else []
1150b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org
1160b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org
1170b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.orgclass ThenNode(SplicingNode):
1180b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org  """A <then> node. Can only appear directly inside an <if> node."""
1190b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org  pass
1200b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org
1210b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org
1220b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.orgclass ElseNode(SplicingNode):
1230b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org  """An <else> node. Can only appear directly inside an <if> node."""
1240b961d0af2fe00a650a882b13752e46c8aef3710benrg@chromium.org  pass
12577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
12783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.orgclass PartNode(SplicingNode):
12883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  """A node for inclusion of sub-grd (*.grp) files.
12983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  """
13083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
13183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  def __init__(self):
13283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    super(PartNode, self).__init__()
13383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.started_inclusion = False
13483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
13583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  def MandatoryAttributes(self):
13683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    return ['file']
13783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
13883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  def _IsValidChild(self, child):
13983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    return self.started_inclusion and super(PartNode, self)._IsValidChild(child)
14083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
14183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
14201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass ReleaseNode(base.Node):
143eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org  """The <release> element."""
14401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
14501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _IsValidChild(self, child):
14601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    from grit.node import empty
14701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return isinstance(child, (empty.IncludesNode, empty.MessagesNode,
14801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                              empty.StructuresNode, empty.IdentifiersNode))
14901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _IsValidAttribute(self, name, value):
15101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return (
15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      (name == 'seq' and int(value) <= self.GetRoot().GetCurrentRelease()) or
15301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      name == 'allow_pseudo'
15401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    )
15501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def MandatoryAttributes(self):
15701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return ['seq']
15801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def DefaultAttributes(self):
16001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return { 'allow_pseudo' : 'true' }
16101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
16201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetReleaseNumber():
163eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the sequence number of this release."""
16401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.attribs['seq']
16501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
16601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass GritNode(base.Node):
167eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org  """The <grit> root element."""
16801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
16901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __init__(self):
170b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    super(GritNode, self).__init__()
17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.output_language = ''
17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.defines = {}
173783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org    self.substituter = None
174abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org    self.target_platform = sys.platform
17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _IsValidChild(self, child):
17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    from grit.node import empty
17801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return isinstance(child, (ReleaseNode, empty.TranslationsNode,
17901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                              empty.OutputsNode))
18001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
18101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _IsValidAttribute(self, name, value):
1820dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    if name not in ['base_dir', 'first_ids_file', 'source_lang_id',
18301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                    'latest_public_release', 'current_release',
184bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org                    'enc_check', 'tc_project', 'grit_version',
185db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org                    'output_all_resource_defines', 'rc_header_format']:
18601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return False
18701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if name in ['latest_public_release', 'current_release'] and value.strip(
18801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      '0123456789') != '':
18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return False
19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return True
19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def MandatoryAttributes(self):
19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return ['latest_public_release', 'current_release']
19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
19501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def DefaultAttributes(self):
19601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return {
19701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      'base_dir' : '.',
1980dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org      'first_ids_file': '',
19977cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      'grit_version': 1,
20001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      'source_lang_id' : 'en',
20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      'enc_check' : constants.ENCODING_CHECK,
20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      'tc_project' : 'NEED_TO_SET_tc_project_ATTRIBUTE',
203db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org      'output_all_resource_defines': 'true',
204db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org      'rc_header_format': None
20501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    }
20601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
20701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def EndParsing(self):
208b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    super(GritNode, self).EndParsing()
20901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if (int(self.attrs['latest_public_release'])
21001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        > int(self.attrs['current_release'])):
21101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      raise exception.Parsing('latest_public_release cannot have a greater '
21201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                              'value than current_release')
21301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.ValidateUniqueIds()
21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Add the encoding check if it's not present (should ensure that it's always
21701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # present in all .grd files generated by GRIT). If it's present, assert if
21801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # it's not correct.
21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if 'enc_check' not in self.attrs or self.attrs['enc_check'] == '':
22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.attrs['enc_check'] = constants.ENCODING_CHECK
22101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
22201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      assert self.attrs['enc_check'] == constants.ENCODING_CHECK, (
22301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        'Are you sure your .grd file is in the correct encoding (UTF-8)?')
22401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
22501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def ValidateUniqueIds(self):
226eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Validate that 'name' attribute is unique in all nodes in this tree
22701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    except for nodes that are children of <if> nodes.
228eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
22901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    unique_names = {}
23001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    duplicate_names = []
231ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    # To avoid false positives from mutually exclusive <if> clauses, check
232ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    # against whatever the output condition happens to be right now.
233ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    # TODO(benrg): do something better.
234ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    for node in self.ActiveDescendants():
235cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org      if node.attrs.get('generateid', 'true') == 'false':
236cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org        continue  # Duplication not relevant in that case
237ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org
238ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org      for node_id in node.GetTextualIds():
239ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org        if util.SYSTEM_IDENTIFIERS.match(node_id):
240ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org          continue  # predefined IDs are sometimes used more than once
241ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org
242ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org        if node_id in unique_names and node_id not in duplicate_names:
243ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org          duplicate_names.append(node_id)
244ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org        unique_names[node_id] = 1
24501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
24601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(duplicate_names):
24701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      raise exception.DuplicateKey(', '.join(duplicate_names))
24801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
24901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
25001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetCurrentRelease(self):
251eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the current release number."""
25201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return int(self.attrs['current_release'])
25301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
25401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetLatestPublicRelease(self):
255eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the latest public release number."""
25601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return int(self.attrs['latest_public_release'])
25701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
25801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetSourceLanguage(self):
259eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the language code of the source language."""
26001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.attrs['source_lang_id']
26101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetTcProject(self):
263eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the name of this project in the TranslationConsole, or
264eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    'NEED_TO_SET_tc_project_ATTRIBUTE' if it is not defined."""
26501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.attrs['tc_project']
26601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def SetOwnDir(self, dir):
268eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Informs the 'grit' element of the directory the file it is in resides.
26901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    This allows it to calculate relative paths from the input file, which is
27001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    what we desire (rather than from the current path).
27101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Args:
27301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      dir: r'c:\bla'
27401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Return:
27601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      None
277eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
27801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    assert dir
27901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.base_dir = os.path.normpath(os.path.join(dir, self.attrs['base_dir']))
28001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
28101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetBaseDir(self):
282eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the base directory, relative to the working directory.  To get
28301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    the base directory as set in the .grd file, use GetOriginalBaseDir()
284eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
28501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if hasattr(self, 'base_dir'):
28601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return self.base_dir
28701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
28801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return self.GetOriginalBaseDir()
28901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
29001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetOriginalBaseDir(self):
291eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the base directory, as set in the .grd file.
292eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
29301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.attrs['base_dir']
29401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
295bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org  def ShouldOutputAllResourceDefines(self):
296bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org    """Returns true if all resource defines should be output, false if
297bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org    defines for resources not emitted to resource files should be
298bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org    skipped.
299bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org    """
300bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org    return self.attrs['output_all_resource_defines'] == 'true'
301bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org
302db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org  def GetRcHeaderFormat(self):
303db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org    return self.attrs['rc_header_format']
304db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org
305db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org  def AssignRcHeaderFormat(self, rc_header_format):
306db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org    self.attrs['rc_header_format'] = rc_header_format
307db2e077842c311c22eb33a13cc9eb9fadb7def5ajoi@chromium.org
308eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org  def GetInputFiles(self):
309eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the list of files that are read to produce the output."""
310eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org
311eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    # Importing this here avoids a circular dependency in the imports.
312eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    # pylint: disable-msg=C6204
313eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    from grit.node import include
31483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    from grit.node import misc
315eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    from grit.node import structure
316eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    from grit.node import variant
317eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org
318ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    # Check if the input is required for any output configuration.
319ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    input_files = set()
320c5a84104bae5837c1be2e879b343af5140baea35benrg@chromium.org    old_output_language = self.output_language
321ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    for lang, ctx in self.GetConfigurations():
322ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org      self.SetOutputLanguage(lang or self.GetSourceLanguage())
323ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org      self.SetOutputContext(ctx)
324ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org      for node in self.ActiveDescendants():
325ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org        if isinstance(node, (io.FileNode, include.IncludeNode, misc.PartNode,
326ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org                             structure.StructureNode, variant.SkeletonNode)):
327ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org          input_files.add(node.GetInputPath())
328c5a84104bae5837c1be2e879b343af5140baea35benrg@chromium.org    self.SetOutputLanguage(old_output_language)
329ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    return sorted(map(self.ToRealPath, input_files))
330eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org
3310dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org  def GetFirstIdsFile(self):
332eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns a usable path to the first_ids file, if set, otherwise
3330dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    returns None.
3340dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org
3350dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    The first_ids_file attribute is by default relative to the
3360dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    base_dir of the .grd file, but may be prefixed by GRIT_DIR/,
3370dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    which makes it relative to the directory of grit.py
3380dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    (e.g. GRIT_DIR/../gritsettings/resource_ids).
339eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
3400dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    if not self.attrs['first_ids_file']:
3410dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org      return None
3420dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org
3430dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    path = self.attrs['first_ids_file']
3445a132756a9c06c4242c1b2a678435fc618a92dcbsergeyu@chromium.org    GRIT_DIR_PREFIX = 'GRIT_DIR'
3455a132756a9c06c4242c1b2a678435fc618a92dcbsergeyu@chromium.org    if (path.startswith(GRIT_DIR_PREFIX)
3465a132756a9c06c4242c1b2a678435fc618a92dcbsergeyu@chromium.org        and path[len(GRIT_DIR_PREFIX)] in ['/', '\\']):
3475a132756a9c06c4242c1b2a678435fc618a92dcbsergeyu@chromium.org      return util.PathFromRoot(path[len(GRIT_DIR_PREFIX) + 1:])
3480dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    else:
3490dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org      return self.ToRealPath(path)
3500dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org
35101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetOutputFiles(self):
352eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the list of <output> nodes that are descendants of this node's
35301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    <outputs> child and are not enclosed by unsatisfied <if> conditionals.
354eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
35501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for child in self.children:
35601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if child.name == 'outputs':
357ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org        return [node for node in child.ActiveDescendants()
358ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org                     if node.name == 'output']
35901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    raise exception.MissingElement()
36001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
361ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  def GetConfigurations(self):
362ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    """Returns the distinct (language, context) pairs from the output nodes.
363ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    """
364ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    return set((n.GetLanguage(), n.GetContext()) for n in self.GetOutputFiles())
365ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org
36677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org  def GetSubstitutionMessages(self):
367eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the list of <message sub_variable="true"> nodes."""
368ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    return [n for n in self.ActiveDescendants()
369ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org            if isinstance(n, message.MessageNode)
370ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org                and n.attrs['sub_variable'] == 'true']
37101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
372783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org  def SetOutputLanguage(self, output_language):
373ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    """Set the output language. Prepares substitutions.
37477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
375ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    The substitutions are reset every time the language is changed.
37677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    They include messages designated as variables, and language codes for html
37777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    and rc files.
37877cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
37977cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    Args:
380cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org      output_language: a two-letter language code (eg: 'en', 'ar'...) or ''
381eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
382783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org    if not output_language:
383783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      # We do not specify the output language for .grh files,
384783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      # so we get an empty string as the default.
385783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      # The value should match grit.clique.MessageClique.source_language.
386783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      output_language = self.GetSourceLanguage()
387783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org    if output_language != self.output_language:
388783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      self.output_language = output_language
389783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      self.substituter = None  # force recalculate
390ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org
391ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org  def SetOutputContext(self, output_context):
392ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self.output_context = output_context
393ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self.substituter = None  # force recalculate
39401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
39501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def SetDefines(self, defines):
39601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.defines = defines
397783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org    self.substituter = None  # force recalculate
398783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org
399abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org  def SetTargetPlatform(self, target_platform):
400abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org    self.target_platform = target_platform
401abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org
402783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org  def GetSubstituter(self):
403783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org    if self.substituter is None:
404783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      self.substituter = util.Substituter()
405783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      self.substituter.AddMessages(self.GetSubstitutionMessages(),
406783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org                                   self.output_language)
407783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      if self.output_language in _RTL_LANGS:
408783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org        direction = 'dir="RTL"'
409783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      else:
410783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org        direction = 'dir="LTR"'
411783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      self.substituter.AddSubstitutions({
412783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org          'GRITLANGCODE': self.output_language,
413783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org          'GRITDIR': direction,
414783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      })
415783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      from grit.format import rc  # avoid circular dep
416783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org      rc.RcSubstitutions(self.substituter, self.output_language)
417783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org    return self.substituter
41801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4190dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org  def AssignFirstIds(self, filename_or_stream, defines):
420eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Assign first ids to each grouping node based on values from the
4210dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    first_ids file (if specified on the <grit> node).
422eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
42301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # If the input is a stream, then we're probably in a unit test and
42401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # should skip this step.
42501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if type(filename_or_stream) not in (str, unicode):
42601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return
42701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4280dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    # Nothing to do if the first_ids_filename attribute isn't set.
4290dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    first_ids_filename = self.GetFirstIdsFile()
4300dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    if not first_ids_filename:
4310dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org      return
4320dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org
4330dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    src_root_dir, first_ids = _ReadFirstIdsFromFile(first_ids_filename,
4340dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                                                    defines)
43501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    from grit.node import empty
436ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    for node in self.Preorder():
43701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if isinstance(node, empty.GroupingNode):
438625ae8faf7992cf919dfc6323d18dc4b6aed04fdjochen@chromium.org        abs_filename = os.path.abspath(filename_or_stream)
439625ae8faf7992cf919dfc6323d18dc4b6aed04fdjochen@chromium.org        if abs_filename[:len(src_root_dir)] != src_root_dir:
440625ae8faf7992cf919dfc6323d18dc4b6aed04fdjochen@chromium.org          filename = os.path.basename(filename_or_stream)
441625ae8faf7992cf919dfc6323d18dc4b6aed04fdjochen@chromium.org        else:
442625ae8faf7992cf919dfc6323d18dc4b6aed04fdjochen@chromium.org          filename = abs_filename[len(src_root_dir) + 1:]
443625ae8faf7992cf919dfc6323d18dc4b6aed04fdjochen@chromium.org          filename = filename.replace('\\', '/')
44401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
44501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        if node.attrs['first_id'] != '':
4460dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org          raise Exception(
4470dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org              "Don't set the first_id attribute when using the first_ids_file "
4480dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org              "attribute on the <grit> node, update %s instead." %
4490dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org              first_ids_filename)
45001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
45101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        try:
45201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          id_list = first_ids[filename][node.name]
45301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        except KeyError, e:
45401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          print '-' * 78
45501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          print 'Resource id not set for %s (%s)!' % (filename, node.name)
45601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          print ('Please update %s to include an entry for %s.  See the '
4570dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                 'comments in resource_ids for information on why you need to '
4580dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                 'update that file.' % (first_ids_filename, filename))
45901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          print '-' * 78
46001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          raise e
46101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
46201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        try:
46301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          node.attrs['first_id'] = str(id_list.pop(0))
46401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        except IndexError, e:
46501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          raise Exception('Please update %s and add a first id for %s (%s).'
4660dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                          % (first_ids_filename, filename, node.name))
46701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
468ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  def RunGatherers(self, debug=False):
469ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    '''Call RunPreSubstitutionGatherer() on every node of the tree, then apply
470ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    substitutions, then call RunPostSubstitutionGatherer() on every node.
47177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
472ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    The substitutions step requires that the output language has been set.
473ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    Locally, get the Substitution messages and add them to the substituter.
474ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    Also add substitutions for language codes in the Rc.
4751eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org
47677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    Args:
47777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      debug: will print information while running gatherers.
478ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    '''
479ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    for node in self.ActiveDescendants():
480ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org      if hasattr(node, 'RunPreSubstitutionGatherer'):
481ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org        with node:
482ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org          node.RunPreSubstitutionGatherer(debug=debug)
4831eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org
484ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    assert self.output_language
485ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    self.SubstituteMessages(self.GetSubstituter())
4861eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org
487ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    for node in self.ActiveDescendants():
488ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org      if hasattr(node, 'RunPostSubstitutionGatherer'):
489ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org        with node:
490ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org          node.RunPostSubstitutionGatherer(debug=debug)
49177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org
49201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
49301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass IdentifierNode(base.Node):
494eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org  """A node for specifying identifiers that should appear in the resource
49501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  header file, and be unique amongst all other resource identifiers, but don't
49601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  have any other attributes or reference any resources.
497eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org  """
49801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
49901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def MandatoryAttributes(self):
50001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return ['name']
50101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
50201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def DefaultAttributes(self):
503cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org    return { 'comment' : '', 'id' : '', 'systemid': 'false' }
50401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
50501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetId(self):
506eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Returns the id of this identifier if it has one, None otherwise
507eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
50801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if 'id' in self.attrs:
50901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return self.attrs['id']
51001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return None
51101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
512cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org  def EndParsing(self):
513eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Handles system identifiers."""
514b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    super(IdentifierNode, self).EndParsing()
515cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org    if self.attrs['systemid'] == 'true':
516cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org      util.SetupSystemIdentifiers((self.attrs['name'],))
517cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org
518cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org  @staticmethod
519cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org  def Construct(parent, name, id, comment, systemid='false'):
520eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """Creates a new node which is a child of 'parent', with attributes set
52101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    by parameters of the same name.
522eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org    """
52301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node = IdentifierNode()
52401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.StartParsing('identifier', parent)
52501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.HandleAttribute('name', name)
52601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.HandleAttribute('id', id)
52701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.HandleAttribute('comment', comment)
528cb59a41015e4a21a5bd865f5646e19d99a3d03aajoi@chromium.org    node.HandleAttribute('systemid', systemid)
52901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node.EndParsing()
53001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return node
531