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'''A baseclass for simple gatherers that store their gathered resource in a 701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orglist. 801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org''' 901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport types 1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.gather import interface 1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import clique 1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import tclib 1501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass SkeletonGatherer(interface.GathererBase): 1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Common functionality of gatherers that parse their input as a skeleton of 1901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translatable and nontranslatable chunks. 2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 22ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org def __init__(self, *args, **kwargs): 23ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org super(SkeletonGatherer, self).__init__(*args, **kwargs) 2401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # List of parts of the document. Translateable parts are 2501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # clique.MessageClique objects, nontranslateable parts are plain strings. 2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Translated messages are inserted back into the skeleton using the quoting 2701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # rules defined by self.Escape() 2801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.skeleton_ = [] 2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # A list of the names of IDs that need to be defined for this resource 3001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # section to compile correctly. 3101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.ids_ = [] 3201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # True if Parse() has already been called. 3301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.have_parsed_ = False 3401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # True if a translatable chunk has been added 3501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.translatable_chunk_ = False 3601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # If not None, all parts of the document will be put into this single 3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # message; otherwise the normal skeleton approach is used. 3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.single_message_ = None 3901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Number to use for the next placeholder name. Used only if single_message 4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # is not None 4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.ph_counter_ = 1 4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 4301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetText(self): 4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the original text of the section''' 4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.text_ 4601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 4701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def Escape(self, text): 4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Subclasses can override. Base impl is identity. 4901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return text 5101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 5201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def UnEscape(self, text): 5301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Subclasses can override. Base impl is identity. 5401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 5501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return text 5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetTextualIds(self): 5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the list of textual IDs that need to be defined for this 5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org resource section to compile correctly.''' 6001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.ids_ 6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def _AddTextualId(self, id): 6301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.ids_.append(id) 6401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 6501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetCliques(self): 6601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the message cliques for each translateable message in the 6701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org resource section.''' 68705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org return [x for x in self.skeleton_ if isinstance(x, clique.MessageClique)] 6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 7001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def Translate(self, lang, pseudo_if_not_available=True, 7101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org skeleton_gatherer=None, fallback_to_english=False): 7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(self.skeleton_) == 0: 7301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.NotReady() 7401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if skeleton_gatherer: 7501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert len(skeleton_gatherer.skeleton_) == len(self.skeleton_) 7601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 7701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org out = [] 7801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for ix in range(len(self.skeleton_)): 7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if isinstance(self.skeleton_[ix], types.StringTypes): 8001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if skeleton_gatherer: 8101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Make sure the skeleton is like the original 8201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert(isinstance(skeleton_gatherer.skeleton_[ix], types.StringTypes)) 8301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org out.append(skeleton_gatherer.skeleton_[ix]) 8401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 8501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org out.append(self.skeleton_[ix]) 8601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 8701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if skeleton_gatherer: # Make sure the skeleton is like the original 8801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert(not isinstance(skeleton_gatherer.skeleton_[ix], 8901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org types.StringTypes)) 9001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org msg = self.skeleton_[ix].MessageForLanguage(lang, 9101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org pseudo_if_not_available, 9201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org fallback_to_english) 9301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 9401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def MyEscape(text): 9501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.Escape(text) 9601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org text = msg.GetRealContent(escaping_function=MyEscape) 9701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org out.append(text) 9801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return ''.join(out) 9901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 10001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def Parse(self): 10101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Parses the section. Implemented by subclasses. Idempotent.''' 10201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise NotImplementedError() 10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def _AddNontranslateableChunk(self, chunk): 10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Adds a nontranslateable chunk.''' 10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.single_message_: 10701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ph = tclib.Placeholder('XX%02dXX' % self.ph_counter_, chunk, chunk) 10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.ph_counter_ += 1 10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.single_message_.AppendPlaceholder(ph) 11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.skeleton_.append(chunk) 11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 11301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def _AddTranslateableChunk(self, chunk): 11401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Adds a translateable chunk. It will be unescaped before being added.''' 11501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # We don't want empty messages since they are redundant and the TC 11601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # doesn't allow them. 11701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if chunk == '': 11801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return 11901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 12001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org unescaped_text = self.UnEscape(chunk) 12101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.single_message_: 12201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.single_message_.AppendText(unescaped_text) 12301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 12401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.skeleton_.append(self.uberclique.MakeClique( 12501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org tclib.Message(text=unescaped_text))) 12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.translatable_chunk_ = True 12777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org 12877cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org def SubstituteMessages(self, substituter): 12977cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org '''Applies substitutions to all messages in the tree. 13077cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org 13177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org Goes through the skeleton and finds all MessageCliques. 13277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org 13377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org Args: 13477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org substituter: a grit.util.Substituter object. 13577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org ''' 13677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org if self.single_message_: 13777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org self.single_message_ = substituter.SubstituteMessage(self.single_message_) 13877cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org new_skel = [] 13977cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org for chunk in self.skeleton_: 14077cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org if isinstance(chunk, clique.MessageClique): 14177cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org old_message = chunk.GetMessage() 14277cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org new_message = substituter.SubstituteMessage(old_message) 14377cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org if new_message is not old_message: 14477cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org new_skel.append(self.uberclique.MakeClique(new_message)) 14577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org continue 14677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org new_skel.append(chunk) 14777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org self.skeleton_ = new_skel 148