| OLD | NEW | 
|---|
|  | (Empty) | 
| 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 '''Fast and efficient parser for XTB files. |  | 
| 7 ''' |  | 
| 8 |  | 
| 9 |  | 
| 10 import sys |  | 
| 11 import xml.sax |  | 
| 12 import xml.sax.handler |  | 
| 13 |  | 
| 14 import grit.node.base |  | 
| 15 |  | 
| 16 |  | 
| 17 class XtbContentHandler(xml.sax.handler.ContentHandler): |  | 
| 18   '''A content handler that calls a given callback function for each |  | 
| 19   translation in the XTB file. |  | 
| 20   ''' |  | 
| 21 |  | 
| 22   def __init__(self, callback, defs=None, debug=False, target_platform=None): |  | 
| 23     self.callback = callback |  | 
| 24     self.debug = debug |  | 
| 25     # 0 if we are not currently parsing a translation, otherwise the message |  | 
| 26     # ID of that translation. |  | 
| 27     self.current_id = 0 |  | 
| 28     # Empty if we are not currently parsing a translation, otherwise the |  | 
| 29     # parts we have for that translation - a list of tuples |  | 
| 30     # (is_placeholder, text) |  | 
| 31     self.current_structure = [] |  | 
| 32     # Set to the language ID when we see the <translationbundle> node. |  | 
| 33     self.language = '' |  | 
| 34     # Keep track of the if block we're inside.  We can't nest ifs. |  | 
| 35     self.if_expr = None |  | 
| 36     # Root defines to be used with if expr. |  | 
| 37     if defs: |  | 
| 38       self.defines = defs |  | 
| 39     else: |  | 
| 40       self.defines = {} |  | 
| 41     # Target platform for build. |  | 
| 42     if target_platform: |  | 
| 43       self.target_platform = target_platform |  | 
| 44     else: |  | 
| 45       self.target_platform = sys.platform |  | 
| 46 |  | 
| 47   def startElement(self, name, attrs): |  | 
| 48     if name == 'translation': |  | 
| 49       assert self.current_id == 0 and len(self.current_structure) == 0, ( |  | 
| 50               "Didn't expect a <translation> element here.") |  | 
| 51       self.current_id = attrs.getValue('id') |  | 
| 52     elif name == 'ph': |  | 
| 53       assert self.current_id != 0, "Didn't expect a <ph> element here." |  | 
| 54       self.current_structure.append((True, attrs.getValue('name'))) |  | 
| 55     elif name == 'translationbundle': |  | 
| 56       self.language = attrs.getValue('lang') |  | 
| 57     elif name in ('if', 'then', 'else'): |  | 
| 58       assert self.if_expr is None, "Can't nest <if> or use <else> in xtb files" |  | 
| 59       self.if_expr = attrs.getValue('expr') |  | 
| 60 |  | 
| 61   def endElement(self, name): |  | 
| 62     if name == 'translation': |  | 
| 63       assert self.current_id != 0 |  | 
| 64 |  | 
| 65       defs = self.defines |  | 
| 66       def pp_ifdef(define): |  | 
| 67         return define in defs |  | 
| 68       def pp_if(define): |  | 
| 69         return define in defs and defs[define] |  | 
| 70 |  | 
| 71       # If we're in an if block, only call the callback (add the translation) |  | 
| 72       # if the expression is True. |  | 
| 73       should_run_callback = True |  | 
| 74       if self.if_expr: |  | 
| 75         should_run_callback = grit.node.base.Node.EvaluateExpression( |  | 
| 76             self.if_expr, self.defines, self.target_platform) |  | 
| 77       if should_run_callback: |  | 
| 78         self.callback(self.current_id, self.current_structure) |  | 
| 79 |  | 
| 80       self.current_id = 0 |  | 
| 81       self.current_structure = [] |  | 
| 82     elif name == 'if': |  | 
| 83       assert self.if_expr is not None |  | 
| 84       self.if_expr = None |  | 
| 85 |  | 
| 86   def characters(self, content): |  | 
| 87     if self.current_id != 0: |  | 
| 88       # We are inside a <translation> node so just add the characters to our |  | 
| 89       # structure. |  | 
| 90       # |  | 
| 91       # This naive way of handling characters is OK because in the XTB format, |  | 
| 92       # <ph> nodes are always empty (always <ph name="XXX"/>) and whitespace |  | 
| 93       # inside the <translation> node should be preserved. |  | 
| 94       self.current_structure.append((False, content)) |  | 
| 95 |  | 
| 96 |  | 
| 97 class XtbErrorHandler(xml.sax.handler.ErrorHandler): |  | 
| 98   def error(self, exception): |  | 
| 99     pass |  | 
| 100 |  | 
| 101   def fatalError(self, exception): |  | 
| 102     raise exception |  | 
| 103 |  | 
| 104   def warning(self, exception): |  | 
| 105     pass |  | 
| 106 |  | 
| 107 |  | 
| 108 def Parse(xtb_file, callback_function, defs=None, debug=False, |  | 
| 109           target_platform=None): |  | 
| 110   '''Parse xtb_file, making a call to callback_function for every translation |  | 
| 111   in the XTB file. |  | 
| 112 |  | 
| 113   The callback function must have the signature as described below.  The 'parts' |  | 
| 114   parameter is a list of tuples (is_placeholder, text).  The 'text' part is |  | 
| 115   either the raw text (if is_placeholder is False) or the name of the placeholde
     r |  | 
| 116   (if is_placeholder is True). |  | 
| 117 |  | 
| 118   Args: |  | 
| 119     xtb_file:           open('fr.xtb') |  | 
| 120     callback_function:  def Callback(msg_id, parts): pass |  | 
| 121     defs:               None, or a dictionary of preprocessor definitions. |  | 
| 122     debug:              Default False. Set True for verbose debug output. |  | 
| 123     target_platform:    None, or a sys.platform-like identifier of the build |  | 
| 124                         target platform. |  | 
| 125 |  | 
| 126   Return: |  | 
| 127     The language of the XTB, e.g. 'fr' |  | 
| 128   ''' |  | 
| 129   # Start by advancing the file pointer past the DOCTYPE thing, as the TC |  | 
| 130   # uses a path to the DTD that only works in Unix. |  | 
| 131   # TODO(joi) Remove this ugly hack by getting the TC gang to change the |  | 
| 132   # XTB files somehow? |  | 
| 133   front_of_file = xtb_file.read(1024) |  | 
| 134   xtb_file.seek(front_of_file.find('<translationbundle')) |  | 
| 135 |  | 
| 136   handler = XtbContentHandler(callback=callback_function, defs=defs, |  | 
| 137                               debug=debug, target_platform=target_platform) |  | 
| 138   xml.sax.parse(xtb_file, handler) |  | 
| 139   assert handler.language != '' |  | 
| 140   return handler.language |  | 
| 141 |  | 
| OLD | NEW | 
|---|