1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6'''Support for ALL_ALL.xml format used by Igoogle plug-ins in Google Desktop.'''
7
8import StringIO
9import re
10import xml.sax
11import xml.sax.handler
12import xml.sax.saxutils
13
14from grit.gather import regexp
15from grit import util
16from grit import tclib
17
18# Placeholders can be defined in strings.xml files by putting the name of the
19# placeholder between [![ and ]!] e.g. <MSG>Hello [![USER]!] how are you<MSG>
20PLACEHOLDER_RE = re.compile('(\[!\[|\]!\])')
21
22
23class IgoogleStringsContentHandler(xml.sax.handler.ContentHandler):
24  '''A very dumb parser for splitting the strings.xml file into translateable
25  and nontranslateable chunks.'''
26
27  def __init__(self, parent):
28    self.curr_elem = ''
29    self.curr_text = ''
30    self.parent = parent
31    self.resource_name = ''
32    self.meaning = ''
33    self.translateable = True
34
35  def startElement(self, name, attrs):
36    if (name != 'messagebundle'):
37      self.curr_elem = name
38
39      attr_names = attrs.getQNames()
40      if 'name' in attr_names:
41        self.resource_name = attrs.getValueByQName('name')
42
43      att_text = []
44      for attr_name in attr_names:
45        att_text.append(' ')
46        att_text.append(attr_name)
47        att_text.append('=')
48        att_text.append(
49          xml.sax.saxutils.quoteattr(attrs.getValueByQName(attr_name)))
50
51      self.parent._AddNontranslateableChunk("<%s%s>" %
52                                            (name, ''.join(att_text)))
53
54  def characters(self, content):
55    if self.curr_elem != '':
56      self.curr_text += content
57
58  def endElement(self, name):
59    if name != 'messagebundle':
60      self.parent.AddMessage(self.curr_text, self.resource_name,
61                             self.meaning, self.translateable)
62      self.parent._AddNontranslateableChunk("</%s>\n" % name)
63      self.curr_elem = ''
64      self.curr_text = ''
65      self.resource_name = ''
66      self.meaning = ''
67      self.translateable = True
68
69  def ignorableWhitespace(self, whitespace):
70    pass
71
72
73class IgoogleStrings(regexp.RegexpGatherer):
74  '''Supports the ALL_ALL.xml format used by Igoogle gadgets.'''
75
76  def AddMessage(self, msgtext, description, meaning, translateable):
77    if msgtext == '':
78      return
79
80    msg = tclib.Message(description=description, meaning=meaning)
81
82    unescaped_text = self.UnEscape(msgtext)
83    parts = PLACEHOLDER_RE.split(unescaped_text)
84    in_placeholder = False
85    for part in parts:
86      if part == '':
87        continue
88      elif part == '[![':
89        in_placeholder = True
90      elif part == ']!]':
91        in_placeholder = False
92      else:
93        if in_placeholder:
94          msg.AppendPlaceholder(tclib.Placeholder(part, '[![%s]!]' % part,
95                                                  '(placeholder)'))
96        else:
97          msg.AppendText(part)
98
99    self.skeleton_.append(
100      self.uberclique.MakeClique(msg, translateable=translateable))
101
102    # if statement needed because this is supposed to be idempotent (so never
103    # set back to false)
104    if translateable:
105      self.translatable_chunk_ = True
106
107  # Although we use the RegexpGatherer base class, we do not use the
108  # _RegExpParse method of that class to implement Parse().  Instead, we
109  # parse using a SAX parser.
110  def Parse(self):
111    if self.have_parsed_:
112      return
113    self.have_parsed_ = True
114
115    self.text_ = self._LoadInputFile().strip()
116    self._AddNontranslateableChunk(u'<messagebundle>\n')
117    stream = StringIO.StringIO(self.text_)
118    handler = IgoogleStringsContentHandler(self)
119    xml.sax.parse(stream, handler)
120    self._AddNontranslateableChunk(u'</messagebundle>\n')
121
122  def Escape(self, text):
123    return util.EncodeCdata(text)
124