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# The tclib module contains tools for aggregating, verifying, and storing
701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# messages destined for the Translation Console, as well as for reading
801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# translations back and outputting them in some desired format.
901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org#
1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# This has been stripped down to include only the functionality needed by grit
1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# for creating Windows .rc and .h files.  These are the only parts needed by
1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# the Chrome build process.
1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport exceptions
1501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.extern import FP
1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# This module assumes that within a bundle no two messages can have the
1901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# same id unless they're identical.
2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# The basic classes defined here for external use are Message and Translation,
2201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# where the former is used for English messages and the latter for
2301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# translations. These classes have a lot of common functionality, as expressed
2401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# by the common parent class BaseMessage. Perhaps the most important
2501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# distinction is that translated text is stored in UTF-8, whereas original text
2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# is stored in whatever encoding the client uses (presumably Latin-1).
2701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# --------------------
2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# The public interface
3001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# --------------------
3101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
3201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Generate message id from message text and meaning string (optional),
3301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# both in utf-8 encoding
3401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org#
3501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgdef GenerateMessageId(message, meaning=''):
3601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  fp = FP.FingerPrint(message)
3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  if meaning:
3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # combine the fingerprints of message and meaning
3901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    fp2 = FP.FingerPrint(meaning)
4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if fp < 0:
4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      fp = fp2 + (fp << 1) + 1
4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
4301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      fp = fp2 + (fp << 1)
4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # To avoid negative ids we strip the high-order bit
4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  return str(fp & 0x7fffffffffffffffL)
4601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# -------------------------------------------------------------------------
4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# The MessageTranslationError class is used to signal tclib-specific errors.
4901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass MessageTranslationError(exceptions.Exception):
5101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __init__(self, args = ''):
5201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.args = args
5301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# -----------------------------------------------------------
5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# The Placeholder class represents a placeholder in a message.
5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass Placeholder(object):
5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # String representation
6001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __str__(self):
6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return '%s, "%s", "%s"' % \
6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org           (self.__presentation, self.__original, self.__example)
6301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Getters
6501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetOriginal(self):
6601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__original
6701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetPresentation(self):
6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__presentation
7001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetExample(self):
7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__example
7301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __eq__(self, other):
7501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.EqualTo(other, strict=1, ignore_trailing_spaces=0)
7601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Equality test
7801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # ignore_trailing_spaces: TC is using varchar to store the
8001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # phrwr fields, as a result of that, the trailing spaces
8101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # are removed by MySQL when the strings are stored into TC:-(
8201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # ignore_trailing_spaces parameter is used to ignore
8301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # trailing spaces during equivalence comparison.
8401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
8501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def EqualTo(self, other, strict = 1, ignore_trailing_spaces = 1):
8601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if type(other) is not Placeholder:
8701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
8801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if StringEquals(self.__presentation, other.__presentation,
8901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                    ignore_trailing_spaces):
9001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not strict or (StringEquals(self.__original, other.__original,
9101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                     ignore_trailing_spaces)  and
9201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                        StringEquals(self.__example, other.__example,
9301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                     ignore_trailing_spaces)):
9401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        return 1
9501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return 0
9601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
9701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
9801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# -----------------------------------------------------------------
9901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# BaseMessage is the common parent class of Message and Translation.
10001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# It is not meant for direct use.
10101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
10201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass BaseMessage(object):
10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Three types of message construction is supported. If the message text is a
10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # simple string with no dynamic content, you can pass it to the constructor
10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # as the "text" parameter. Otherwise, you can omit "text" and assemble the
10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # message step by step using AppendText() and AppendPlaceholder(). Or, as an
10701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # alternative, you can give the constructor the "presentable" version of the
10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # message and a list of placeholders; it will then parse the presentation and
10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # build the message accordingly. For example:
11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Message(text = "There are NUM_BUGS bugs in your code",
11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         placeholders = [Placeholder("NUM_BUGS", "%d", "33")],
11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         description = "Bla bla bla")
11301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __eq__(self, other):
11401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # "source encoding" is nonsense, so ignore it
11501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return _ObjectEquals(self, other, ['_BaseMessage__source_encoding'])
11601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
11701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetName(self):
11801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__name
11901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
12001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetSourceEncoding(self):
12101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__source_encoding
12201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
12301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Append a placeholder to the message
12401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def AppendPlaceholder(self, placeholder):
12501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if not isinstance(placeholder, Placeholder):
12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      raise MessageTranslationError, ("Invalid message placeholder %s in "
12701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                      "message %s" % (placeholder, self.GetId()))
12801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Are there other placeholders with the same presentation?
12901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # If so, they need to be the same.
13001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for other in self.GetPlaceholders():
13101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if placeholder.GetPresentation() == other.GetPresentation():
13201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        if not placeholder.EqualTo(other):
13301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          raise MessageTranslationError, \
13401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                "Conflicting declarations of %s within message" % \
13501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                placeholder.GetPresentation()
13601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # update placeholder list
13701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    dup = 0
13801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for item in self.__content:
13901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if isinstance(item, Placeholder) and placeholder.EqualTo(item):
14001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        dup = 1
14101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        break
14201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if not dup:
14301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.__placeholders.append(placeholder)
14401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
14501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # update content
14601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.__content.append(placeholder)
14701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
14801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Strips leading and trailing whitespace, and returns a tuple
14901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # containing the leading and trailing space that was removed.
15001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Strip(self):
15101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    leading = trailing = ''
15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(self.__content) > 0:
15301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      s0 = self.__content[0]
15401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not isinstance(s0, Placeholder):
15501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        s = s0.lstrip()
15601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        leading = s0[:-len(s)]
15701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.__content[0] = s
15801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      s0 = self.__content[-1]
16001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not isinstance(s0, Placeholder):
16101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        s = s0.rstrip()
16201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        trailing = s0[len(s):]
16301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.__content[-1] = s
16401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return leading, trailing
16501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
16601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return the id of this message
16701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetId(self):
16801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.__id is None:
16901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return self.GenerateId()
17001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__id
17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Set the id of this message
17301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def SetId(self, id):
17401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if id is None:
17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.__id = None
17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.__id = str(id)  # Treat numerical ids as strings
17801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return content of this message as a list (internal use only)
18001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetContent(self):
18101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__content
18201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
18301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return a human-readable version of this message
18401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetPresentableContent(self):
18501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    presentable_content = ""
18601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for item in self.__content:
18701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if isinstance(item, Placeholder):
18801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        presentable_content += item.GetPresentation()
18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      else:
19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        presentable_content += item
19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return presentable_content
19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return a fragment of a message in escaped format
19501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def EscapeFragment(self, fragment):
19601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return fragment.replace('%', '%%')
19701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
19801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return the "original" version of this message, doing %-escaping
19901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # properly.  If source_msg is specified, the placeholder original
20001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # information inside source_msg will be used instead.
20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetOriginalContent(self, source_msg = None):
20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    original_content = ""
20301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for item in self.__content:
20401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if isinstance(item, Placeholder):
20501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        if source_msg:
20601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          ph = source_msg.GetPlaceholder(item.GetPresentation())
20701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          if not ph:
20801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org            raise MessageTranslationError, \
20901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                  "Placeholder %s doesn't exist in message: %s" % \
21001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                  (item.GetPresentation(), source_msg);
21101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          original_content += ph.GetOriginal()
21201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        else:
21301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          original_content += item.GetOriginal()
21401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      else:
21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        original_content += self.EscapeFragment(item)
21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return original_content
21701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return the example of this message
21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetExampleContent(self):
22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    example_content = ""
22101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for item in self.__content:
22201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if isinstance(item, Placeholder):
22301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        example_content += item.GetExample()
22401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      else:
22501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        example_content += item
22601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return example_content
22701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
22801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return a list of all unique placeholders in this message
22901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetPlaceholders(self):
23001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__placeholders
23101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
23201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return a placeholder in this message
23301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetPlaceholder(self, presentation):
23401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for item in self.__content:
23501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if (isinstance(item, Placeholder) and
23601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          item.GetPresentation() == presentation):
23701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        return item
23801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return None
23901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
24001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return this message's description
24101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetDescription(self):
24201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__description
24301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
24401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Add a message source
24501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def AddSource(self, source):
24601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.__sources.append(source)
24701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
24801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return this message's sources as a list
24901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetSources(self):
25001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__sources
25101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
25201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return this message's sources as a string
25301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetSourcesAsText(self, delimiter = "; "):
25401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return delimiter.join(self.__sources)
25501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
25601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Set the obsolete flag for a message (internal use only)
25701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def SetObsolete(self):
25801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.__obsolete = 1
25901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Get the obsolete flag for a message (internal use only)
26101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def IsObsolete(self):
26201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__obsolete
26301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Get the sequence number (0 by default)
26501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetSequenceNumber(self):
26601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__sequence_number
26701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Set the sequence number
26901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def SetSequenceNumber(self, number):
27001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.__sequence_number = number
27101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Increment instance counter
27301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def AddInstance(self):
27401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.__num_instances += 1
27501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Return instance count
27701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetNumInstances(self):
27801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__num_instances
27901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
28001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetErrors(self, from_tc=0):
28101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
28201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Returns a description of the problem if the message is not
28301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    syntactically valid, or None if everything is fine.
28401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
28501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Args:
28601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      from_tc: indicates whether this message came from the TC. We let
28701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      the TC get away with some things we normally wouldn't allow for
28801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      historical reasons.
28901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
29001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # check that placeholders are unambiguous
29101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    pos = 0
29201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    phs = {}
29301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for item in self.__content:
29401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if isinstance(item, Placeholder):
29501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        phs[pos] = item
29601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        pos += len(item.GetPresentation())
29701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      else:
29801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        pos += len(item)
29901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    presentation = self.GetPresentableContent()
30001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for ph in self.GetPlaceholders():
30101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      for pos in FindOverlapping(presentation, ph.GetPresentation()):
30201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        # message contains the same text as a placeholder presentation
30301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        other_ph = phs.get(pos)
30401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        if ((not other_ph
30501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org             and not IsSubstringInPlaceholder(pos, len(ph.GetPresentation()), phs))
30601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org            or
30701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org            (other_ph and len(other_ph.GetPresentation()) < len(ph.GetPresentation()))):
30801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          return  "message contains placeholder name '%s':\n%s" % (
30901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org            ph.GetPresentation(), presentation)
31001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return None
31101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
31201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
31301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __CopyTo(self, other):
31401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
31501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Returns a copy of this BaseMessage.
31601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
31701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    assert isinstance(other,  self.__class__) or isinstance(self, other.__class__)
31801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__source_encoding = self.__source_encoding
31901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__content         = self.__content[:]
32001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__description     = self.__description
32101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__id              = self.__id
32201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__num_instances   = self.__num_instances
32301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__obsolete        = self.__obsolete
32401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__name            = self.__name
32501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__placeholders    = self.__placeholders[:]
32601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__sequence_number = self.__sequence_number
32701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    other.__sources         = self.__sources[:]
32801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
32901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return other
33001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
33101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def HasText(self):
33201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """Returns true iff this message has anything other than placeholders."""
33301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for item in self.__content:
33401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not isinstance(item, Placeholder):
33501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        return True
33601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return False
33701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
33801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# --------------------------------------------------------
33901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# The Message class represents original (English) messages
34001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
34101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass Message(BaseMessage):
34201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # See BaseMessage constructor
34301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __init__(self, source_encoding, text=None, id=None,
34401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org               description=None, meaning="", placeholders=None,
34501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org               source=None, sequence_number=0, clone_from=None,
34601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org               time_created=0, name=None, is_hidden = 0):
34701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
34801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if clone_from is not None:
34901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      BaseMessage.__init__(self, None, clone_from=clone_from)
35001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.__meaning = clone_from.__meaning
35101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.__time_created = clone_from.__time_created
35201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.__is_hidden = clone_from.__is_hidden
35301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return
35401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
35501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    BaseMessage.__init__(self, source_encoding, text, id, description,
35601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                         placeholders, source, sequence_number,
35701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                         name=name)
35801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.__meaning = meaning
35901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.__time_created = time_created
36001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.SetIsHidden(is_hidden)
36101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
36201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # String representation
36301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __str__(self):
36401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    s = 'source: %s, id: %s, content: "%s", meaning: "%s", ' \
36501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        'description: "%s"' % \
36601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        (self.GetSourcesAsText(), self.GetId(), self.GetPresentableContent(),
36701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org         self.__meaning, self.GetDescription())
36801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.GetName() is not None:
36901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      s += ', name: "%s"' % self.GetName()
37001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    placeholders = self.GetPlaceholders()
37101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for i in range(len(placeholders)):
37201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      s += ", placeholder[%d]: %s" % (i, placeholders[i])
37301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return s
37401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
37501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Strips leading and trailing whitespace, and returns a tuple
37601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # containing the leading and trailing space that was removed.
37701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Strip(self):
37801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    leading = trailing = ''
37901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    content = self.GetContent()
38001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(content) > 0:
38101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      s0 = content[0]
38201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not isinstance(s0, Placeholder):
38301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        s = s0.lstrip()
38401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        leading = s0[:-len(s)]
38501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        content[0] = s
38601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
38701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      s0 = content[-1]
38801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not isinstance(s0, Placeholder):
38901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        s = s0.rstrip()
39001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        trailing = s0[len(s):]
39101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        content[-1] = s
39201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return leading, trailing
39301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
39401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Generate an id by hashing message content
39501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GenerateId(self):
39601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.SetId(GenerateMessageId(self.GetPresentableContent(),
39701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                                 self.__meaning))
39801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.GetId()
39901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
40001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetMeaning(self):
40101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__meaning
40201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
40301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def GetTimeCreated(self):
40401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__time_created
40501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
40601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Equality operator
40701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def EqualTo(self, other, strict = 1):
40801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Check id, meaning, content
40901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.GetId() != other.GetId():
41001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
41101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.__meaning != other.__meaning:
41201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
41301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.GetPresentableContent() != other.GetPresentableContent():
41401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
41501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Check descriptions if comparison is strict
41601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if (strict and
41701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.GetDescription() is not None and
41801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        other.GetDescription() is not None and
41901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.GetDescription() != other.GetDescription()):
42001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
42101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Check placeholders
42201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ph1 = self.GetPlaceholders()
42301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ph2 = other.GetPlaceholders()
42401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(ph1) != len(ph2):
42501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
42601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for i in range(len(ph1)):
42701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not ph1[i].EqualTo(ph2[i], strict):
42801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        return 0
42901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
43001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return 1
43101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
43201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Copy(self):
43301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
43401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Returns a copy of this Message.
43501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
43601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    assert isinstance(self, Message)
43701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return Message(None, clone_from=self)
43801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
43901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def SetIsHidden(self, is_hidden):
44001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """Sets whether this message should be hidden.
44101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
44201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Args:
44301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      is_hidden : 0 or 1 - if the message should be hidden, 0 otherwise
44401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
44501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if is_hidden not in [0, 1]:
44601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      raise  MessageTranslationError, "is_hidden must be 0 or 1, got %s"
44701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.__is_hidden = is_hidden
44801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
44901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def IsHidden(self):
45001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """Returns 1 if this message is hidden, and 0 otherwise."""
45101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return self.__is_hidden
45201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
45301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# ----------------------------------------------------
45401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# The Translation class represents translated messages
45501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
45601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass Translation(BaseMessage):
45701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # See BaseMessage constructor
45801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __init__(self, source_encoding, text=None, id=None,
45901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org               description=None, placeholders=None, source=None,
46001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org               sequence_number=0, clone_from=None, ignore_ph_errors=0,
46101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org               name=None):
46201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if clone_from is not None:
46301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      BaseMessage.__init__(self, None, clone_from=clone_from)
46401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return
46501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
46601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    BaseMessage.__init__(self, source_encoding, text, id, description,
46701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                         placeholders, source, sequence_number,
46801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                         ignore_ph_errors=ignore_ph_errors, name=name)
46901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
47001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # String representation
47101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def __str__(self):
47201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    s = 'source: %s, id: %s, content: "%s", description: "%s"' % \
47301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        (self.GetSourcesAsText(), self.GetId(), self.GetPresentableContent(),
47401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org         self.GetDescription());
47501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    placeholders = self.GetPlaceholders()
47601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for i in range(len(placeholders)):
47701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      s += ", placeholder[%d]: %s" % (i, placeholders[i])
47801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return s
47901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
48001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Equality operator
48101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def EqualTo(self, other, strict=1):
48201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Check id and content
48301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.GetId() != other.GetId():
48401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
48501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.GetPresentableContent() != other.GetPresentableContent():
48601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
48701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Check placeholders
48801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ph1 = self.GetPlaceholders()
48901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ph2 = other.GetPlaceholders()
49001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(ph1) != len(ph2):
49101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 0
49201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for i in range(len(ph1)):
49301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not ph1[i].EqualTo(ph2[i], strict):
49401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        return 0
49501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
49601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return 1
49701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
49801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Copy(self):
49901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
50001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Returns a copy of this Translation.
50101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    """
50201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return Translation(None, clone_from=self)
50301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
504