| LEFT | RIGHT |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
| 3 # | 3 # |
| 4 # Copyright 2006-2008 Zuza Software Foundation | 4 # Copyright 2006-2008 Zuza Software Foundation |
| 5 # | 5 # |
| 6 # This file is part of translate. | 6 # This file is part of translate. |
| 7 # | 7 # |
| 8 # translate is free software; you can redistribute it and/or modify | 8 # translate is free software; you can redistribute it and/or modify |
| 9 # it under the terms of the GNU General Public License as published by | 9 # it under the terms of the GNU General Public License as published by |
| 10 # the Free Software Foundation; either version 2 of the License, or | 10 # the Free Software Foundation; either version 2 of the License, or |
| 11 # (at your option) any later version. | 11 # (at your option) any later version. |
| 12 # | 12 # |
| 13 # translate is distributed in the hope that it will be useful, | 13 # translate is distributed in the hope that it will be useful, |
| 14 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 # GNU General Public License for more details. | 16 # GNU General Public License for more details. |
| 17 # | 17 # |
| 18 # You should have received a copy of the GNU General Public License | 18 # You should have received a copy of the GNU General Public License |
| 19 # along with translate; if not, write to the Free Software | 19 # along with translate; if not, write to the Free Software |
| 20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 21 | 21 |
| 22 """Base classes for storage interfaces. | 22 """Base classes for storage interfaces. |
| 23 | 23 |
| 24 @organization: Zuza Software Foundation | 24 @organization: Zuza Software Foundation |
| 25 @copyright: 2006-2007 Zuza Software Foundation | 25 @copyright: 2006-2007 Zuza Software Foundation |
| 26 @license: U{GPL <http://www.fsf.org/licensing/licenses/gpl.html>} | 26 @license: U{GPL <http://www.fsf.org/licensing/licenses/gpl.html>} |
| 27 """ | 27 """ |
| 28 | 28 |
| 29 try: | 29 try: |
| 30 import cPickle as pickle | 30 import cPickle as pickle |
| 31 except: | 31 except: |
| 32 import pickle | 32 import pickle |
| 33 from exceptions import NotImplementedError | 33 from exceptions import NotImplementedError |
| 34 | 34 |
| 35 def force_override(method, baseclass): | 35 def force_override(method, baseclass): |
| 36 """Forces derived classes to override method.""" | 36 """Forces derived classes to override method.""" |
| 37 | 37 |
| 38 if type(method.im_self) == type(baseclass): | 38 if type(method.im_self) == type(baseclass): |
| 39 # then this is a classmethod and im_self is the actual class | 39 # then this is a classmethod and im_self is the actual class |
| 40 actualclass = method.im_self | 40 actualclass = method.im_self |
| 41 else: | 41 else: |
| 42 actualclass = method.im_class | 42 actualclass = method.im_class |
| 43 if actualclass != baseclass: | 43 if actualclass != baseclass: |
| 44 raise NotImplementedError("%s does not reimplement %s as required by %s" % (actualclass.__name__, method.__name__, baseclass.__name__)) | 44 raise NotImplementedError("%s does not reimplement %s as required by %s" % (actualclass.__name__, method.__name__, baseclass.__name__)) |
| 45 | 45 |
| 46 class ParseError(Exception): | 46 class ParseError(Exception): |
| 47 pass | 47 pass |
| 48 | 48 |
| 49 class TranslationUnit(object): | 49 class TranslationUnit(object): |
| 50 """Base class for translation units. | 50 """Base class for translation units. |
| 51 | 51 |
| 52 Our concept of a I{translation unit} is influenced heavily by XLIFF: | 52 Our concept of a I{translation unit} is influenced heavily by XLIFF: |
| 53 U{http://www.oasis-open.org/committees/xliff/documents/xliff-specification.h tm} | 53 U{http://www.oasis-open.org/committees/xliff/documents/xliff-specification.h tm} |
| 54 | 54 |
| 55 As such most of the method- and variable names borrows from XLIFF terminolog y. | 55 As such most of the method- and variable names borrows from XLIFF terminolog y. |
| 56 | 56 |
| 57 A translation unit consists of the following: | 57 A translation unit consists of the following: |
| 58 - A I{source} string. This is the original translatable text. | 58 - A I{source} string. This is the original translatable text. |
| 59 - A I{target} string. This is the translation of the I{source}. | 59 - A I{target} string. This is the translation of the I{source}. |
| 60 - Zero or more I{notes} on the unit. Notes would typically be some | 60 - Zero or more I{notes} on the unit. Notes would typically be some |
| 61 comments from a translator on the unit, or some comments originating fro m | 61 comments from a translator on the unit, or some comments originating fro m |
| 62 the source code. | 62 the source code. |
| 63 - Zero or more I{locations}. Locations indicate where in the original | 63 - Zero or more I{locations}. Locations indicate where in the original |
| 64 source code this unit came from. | 64 source code this unit came from. |
| 65 - Zero or more I{errors}. Some tools (eg. L{pofilter <filters.pofilter>}) can run checks on | 65 - Zero or more I{errors}. Some tools (eg. L{pofilter <filters.pofilter>}) can run checks on |
| 66 translations and produce error messages. | 66 translations and produce error messages. |
| 67 - Zero or one I{prev_source} string, the old version of the translatable t ext. | |
| 68 - Zero or one I{prev_context} string, the old version of the message conte xt. | |
| 67 | 69 |
| 68 @group Source: *source* | 70 @group Source: *source* |
| 69 @group Target: *target* | 71 @group Target: *target* |
| 72 @group PrevSource: *PrevSource* | |
| 73 @group PrevContext: *PrevContext* | |
| 70 @group Notes: *note* | 74 @group Notes: *note* |
| 71 @group Locations: *location* | 75 @group Locations: *location* |
| 72 @group Errors: *error* | 76 @group Errors: *error* |
| 73 """ | 77 """ |
| 74 | 78 |
| 75 def __init__(self, source): | 79 def __init__(self, source): |
| 76 """Constructs a TranslationUnit containing the given source string.""" | 80 """Constructs a TranslationUnit containing the given source string.""" |
| 77 | 81 |
| 78 self._store = None | |
| 79 self.source = source | 82 self.source = source |
| 80 self.target = None | 83 self.target = None |
| 84 self.prev_source = None | |
| 85 self.prev_context = None | |
| 81 self.notes = "" | 86 self.notes = "" |
| 82 super(TranslationUnit, self).__init__() | 87 super(TranslationUnit, self).__init__() |
| 83 | 88 |
| 84 def __eq__(self, other): | 89 def __eq__(self, other): |
| 85 """Compares two TranslationUnits. | 90 """Compares two TranslationUnits. |
| 86 | 91 |
| 87 @type other: L{TranslationUnit} | 92 @type other: L{TranslationUnit} |
| 88 @param other: Another L{TranslationUnit} | 93 @param other: Another L{TranslationUnit} |
| 89 @rtype: Boolean | 94 @rtype: Boolean |
| 90 @return: Returns True if the supplied TranslationUnit equals this unit. | 95 @return: Returns True if the supplied TranslationUnit equals this unit. |
| 91 | 96 |
| 92 """ | 97 """ |
| 93 | 98 |
| 94 return self.source == other.source and self.target == other.target | 99 return self.source == other.source and self.target == other.target |
| 95 | 100 |
| 96 def settarget(self, target): | 101 def settarget(self, target): |
| 97 """Sets the target string to the given value.""" | 102 """Sets the target string to the given value.""" |
| 98 | 103 |
| 99 self.target = target | 104 self.target = target |
| 100 | 105 |
| 101 def gettargetlen(self): | 106 def gettargetlen(self): |
| 102 """Returns the length of the target string. | 107 """Returns the length of the target string. |
| 103 | 108 |
| 104 @note: Plural forms might be combined. | 109 @note: Plural forms might be combined. |
| 105 @rtype: Integer | 110 @rtype: Integer |
| 106 | 111 |
| 107 """ | 112 """ |
| 108 | 113 |
| 109 length = len(self.target or "") | 114 length = len(self.target or "") |
| 110 strings = getattr(self.target, "strings", []) | 115 strings = getattr(self.target, "strings", []) |
| 111 if strings: | 116 if strings: |
| 112 length += sum([len(pluralform) for pluralform in strings[1:]]) | 117 length += sum([len(pluralform) for pluralform in strings[1:]]) |
| 113 return length | 118 return length |
| 114 | 119 |
| 115 def getid(self): | 120 def getid(self): |
| 116 """A unique identifier for this unit. | 121 """A unique identifier for this unit. |
| 117 | 122 |
| 118 @rtype: string | 123 @rtype: string |
| 119 @return: an identifier for this unit that is unique in the store | 124 @return: an identifier for this unit that is unique in the store |
| 120 | 125 |
| 121 Derived classes should override this in a way that guarantees a unique | 126 Derived classes should override this in a way that guarantees a unique |
| 122 identifier for each unit in the store. | 127 identifier for each unit in the store. |
| 123 """ | 128 """ |
| 124 return self.source | 129 return self.source |
| 125 | 130 |
| 126 def getlocations(self): | 131 def getlocations(self): |
| 127 """A list of source code locations. | 132 """A list of source code locations. |
| 128 | 133 |
| 129 @note: Shouldn't be implemented if the format doesn't support it. | 134 @note: Shouldn't be implemented if the format doesn't support it. |
| 130 @rtype: List | 135 @rtype: List |
| (...skipping 99 matching lines...) Show 10 above Show 10 below | |
| 230 to ensure that other classes can implement more functionality | 235 to ensure that other classes can implement more functionality |
| 231 (as XLIFF does). | 236 (as XLIFF does). |
| 232 | 237 |
| 233 """ | 238 """ |
| 234 | 239 |
| 235 return bool(self.target) and not self.isfuzzy() | 240 return bool(self.target) and not self.isfuzzy() |
| 236 | 241 |
| 237 def istranslatable(self): | 242 def istranslatable(self): |
| 238 """Indicates whether this unit can be translated. | 243 """Indicates whether this unit can be translated. |
| 239 | 244 |
| 240 This should be used to distinguish real units for translation from | 245 This should be used to distinguish real units for translation from |
| 241 header, obsolete, binary or other blank units. | 246 header, obsolete, binary or other blank units. |
| 242 """ | 247 """ |
| 243 return True | 248 return True |
| 244 | 249 |
| 245 def isfuzzy(self): | 250 def isfuzzy(self): |
| 246 """Indicates whether this unit is fuzzy.""" | 251 """Indicates whether this unit is fuzzy.""" |
| 247 | 252 |
| 248 return False | 253 return False |
| 249 | 254 |
| 250 def markfuzzy(self, value=True): | 255 def markfuzzy(self, value=True): |
| 251 """Marks the unit as fuzzy or not.""" | 256 """Marks the unit as fuzzy or not.""" |
| 252 pass | 257 pass |
| 253 | 258 |
| 254 def isheader(self): | 259 def isheader(self): |
| 255 """Indicates whether this unit is a header.""" | 260 """Indicates whether this unit is a header.""" |
| 256 | 261 |
| 257 return False | 262 return False |
| 258 | 263 |
| 259 def isreview(self): | 264 def isreview(self): |
| 260 """Indicates whether this unit needs review.""" | 265 """Indicates whether this unit needs review.""" |
| 261 return False | 266 return False |
| 262 | 267 |
| 263 | 268 |
| 264 def isblank(self): | 269 def isblank(self): |
| 265 """Used to see if this unit has no source or target string. | 270 """Used to see if this unit has no source or target string. |
| 266 | 271 |
| 267 @note: This is probably used more to find translatable units, | 272 @note: This is probably used more to find translatable units, |
| 268 and we might want to move in that direction rather and get rid of this. | 273 and we might want to move in that direction rather and get rid of this. |
| 269 | 274 |
| 270 """ | 275 """ |
| 271 | 276 |
| 272 return not (self.source or self.target) | 277 return not (self.source or self.target) |
| 273 | 278 |
| 274 def hasplural(self): | 279 def hasplural(self): |
| 275 """Tells whether or not this specific unit has plural strings.""" | 280 """Tells whether or not this specific unit has plural strings.""" |
| 276 | 281 |
| 277 #TODO: Reconsider | 282 #TODO: Reconsider |
| 278 return False | 283 return False |
| 279 | 284 |
| 280 def getsourcelanguage(self): | |
| 281 return getattr(self._store, "sourcelanguage", "en") | |
| 282 | |
| 283 def gettargetlanguage(self): | |
| 284 return getattr(self._store, "targetlanguage", None) | |
| 285 | |
| 286 def merge(self, otherunit, overwrite=False, comments=True): | 285 def merge(self, otherunit, overwrite=False, comments=True): |
| 287 """Do basic format agnostic merging.""" | 286 """Do basic format agnostic merging.""" |
| 288 | 287 |
| 289 if self.target == "" or overwrite: | 288 if self.target == "" or overwrite: |
| 290 self.target = otherunit.target | 289 self.target = otherunit.target |
| 291 | 290 |
| 292 def unit_iter(self): | 291 def unit_iter(self): |
| 293 """Iterator that only returns this unit.""" | 292 """Iterator that only returns this unit.""" |
| 294 yield self | 293 yield self |
| 295 | 294 |
| 296 def getunits(self): | 295 def getunits(self): |
| 297 """This unit in a list.""" | 296 """This unit in a list.""" |
| 298 return [self] | 297 return [self] |
| 299 | 298 |
| 300 def setprev_source(self, source): | 299 def setprev_source(self, source): |
| 301 """Sets the previous source message for this unit""" | 300 """Sets the previous source message for this unit""" |
| 302 pass | 301 self.prev_source = source |
| 303 | 302 |
| 304 def getprev_source(self): | 303 def getprev_source(self): |
| 305 """Gets the previous source message for this unit""" | 304 """Gets the previous source message for this unit""" |
| 306 pass | 305 return self.prev_source |
|
dwaynebailey
2008/07/17 12:59:27
Shouldn't the decorators be here for prev_source?
georgeyk
2008/07/18 05:15:49
On 2008/07/17 12:59:27, dwaynebailey wrote:
> Shou
| |
| 307 | |
| 308 prev_source = property(getprev_source, setprev_source) | |
| 309 | 306 |
| 310 def setprev_context(self, context): | 307 def setprev_context(self, context): |
| 311 """Sets the previous context of this unit""" | 308 """Sets the previous context of this unit""" |
| 312 pass | 309 self.prev_context = context |
| 313 | 310 |
| 314 def getprev_context(self): | 311 def getprev_context(self): |
| 315 """Gets the previous context of this unit""" | 312 """Gets the previous context of this unit""" |
| 316 pass | 313 return self.prev_context |
| 317 | |
| 318 prev_context = property(getprev_context, setprev_context) | |
| 319 | 314 |
| 320 def set_as_previous(self): | 315 def set_as_previous(self): |
| 321 """Sets this unit as a previous unit message""" | 316 """Sets this unit as a previous unit message""" |
| 322 if self.isfuzzy(): | 317 if self.isfuzzy(): |
| 323 return | 318 return |
| 324 self.setprev_context(self.getcontext()) | 319 self.setprev_context(self.getcontext()) |
| 325 self.setprev_source(self.source) | 320 self.setprev_source(self.source) |
| 326 self.markfuzzy() | 321 self.markfuzzy() |
| 327 | 322 |
| 328 def buildfromunit(cls, unit): | 323 def buildfromunit(cls, unit): |
| 329 """Build a native unit from a foreign unit, preserving as much | 324 """Build a native unit from a foreign unit, preserving as much |
| 330 information as possible.""" | 325 information as possible.""" |
| 331 | 326 |
| 332 if type(unit) == cls and hasattr(unit, "copy") and callable(unit.copy): | 327 if type(unit) == cls and hasattr(unit, "copy") and callable(unit.copy): |
| 333 return unit.copy() | 328 return unit.copy() |
| 334 newunit = cls(unit.source) | 329 newunit = cls(unit.source) |
| 335 newunit.target = unit.target | 330 newunit.target = unit.target |
| 336 newunit.markfuzzy(unit.isfuzzy()) | 331 newunit.markfuzzy(unit.isfuzzy()) |
| 337 locations = unit.getlocations() | 332 locations = unit.getlocations() |
| 338 if locations: | 333 if locations: |
| 339 newunit.addlocations(locations) | 334 newunit.addlocations(locations) |
| 340 notes = unit.getnotes() | 335 notes = unit.getnotes() |
| 341 if notes: | 336 if notes: |
| 342 newunit.addnote(notes) | 337 newunit.addnote(notes) |
| 343 return newunit | 338 return newunit |
| 344 buildfromunit = classmethod(buildfromunit) | 339 buildfromunit = classmethod(buildfromunit) |
| 345 | 340 |
| 346 class TranslationStore(object): | 341 class TranslationStore(object): |
| 347 """Base class for stores for multiple translation units of type UnitClass."" " | 342 """Base class for stores for multiple translation units of type UnitClass."" " |
| 348 | 343 |
| 349 UnitClass = TranslationUnit | 344 UnitClass = TranslationUnit |
| 350 Mimetypes = None | 345 Mimetypes = None |
| 351 Extensions = None | 346 Extensions = None |
| 352 | 347 |
| 353 def __init__(self, unitclass=None): | 348 def __init__(self, unitclass=None): |
| 354 """Constructs a blank TranslationStore.""" | 349 """Constructs a blank TranslationStore.""" |
| 355 | 350 |
| 356 self.units = [] | 351 self.units = [] |
| 357 self.filepath = None | 352 self.filepath = None |
| 358 self.translator = "" | 353 self.translator = "" |
| 359 self.date = "" | 354 self.date = "" |
| 360 self.sourcelanguage = None | 355 self.sourcelanguage = None |
| 361 self.targetlanguage = None | 356 self.targetlanguage = None |
| 362 if unitclass: | 357 if unitclass: |
| 363 self.UnitClass = unitclass | 358 self.UnitClass = unitclass |
| 364 super(TranslationStore, self).__init__() | 359 super(TranslationStore, self).__init__() |
| 365 | 360 |
| 366 def setsourcelanguage(self, sourcelanguage): | 361 def setsourcelanguage(self, sourcelanguage): |
| 367 """Sets the source language for this store""" | 362 """Sets the source language for this store""" |
| 368 self.sourcelanguage = sourcelanguage | 363 self.sourcelanguage = sourcelanguage |
| 369 | 364 |
| 370 def settargetlanguage(self, targetlanguage): | 365 def settargetlanguage(self, targetlanguage): |
| 371 """Sets the target language for this store""" | 366 """Sets the target language for this store""" |
| 372 self.targetlanguage = targetlanguage | 367 self.targetlanguage = targetlanguage |
| 373 | 368 |
| 374 def unit_iter(self): | 369 def unit_iter(self): |
| 375 """Iterator over all the units in this store.""" | 370 """Iterator over all the units in this store.""" |
| 376 for unit in self.units: | 371 for unit in self.units: |
| 377 yield unit | 372 yield unit |
| 378 | 373 |
| 379 def getunits(self): | 374 def getunits(self): |
| 380 """Return a list of all units in this store.""" | 375 """Return a list of all units in this store.""" |
| 381 return [unit for unit in self.unit_iter()] | 376 return [unit for unit in self.unit_iter()] |
| 382 | 377 |
| 383 def addunit(self, unit): | 378 def addunit(self, unit): |
| 384 """Appends the given unit to the object's list of units. | 379 """Appends the given unit to the object's list of units. |
| 385 | 380 |
| 386 This method should always be used rather than trying to modify the | 381 This method should always be used rather than trying to modify the |
| 387 list manually. | 382 list manually. |
| 388 | 383 |
| 389 @type unit: L{TranslationUnit} | 384 @type unit: L{TranslationUnit} |
| 390 @param unit: The unit that will be added. | 385 @param unit: The unit that will be added. |
| 391 | 386 |
| 392 """ | 387 """ |
| 393 unit._store = self | 388 |
| 394 self.units.append(unit) | 389 self.units.append(unit) |
| 395 | 390 |
| 396 def addsourceunit(self, source): | 391 def addsourceunit(self, source): |
| 397 """Adds and returns a new unit with the given source string. | 392 """Adds and returns a new unit with the given source string. |
| 398 | 393 |
| 399 @rtype: L{TranslationUnit} | 394 @rtype: L{TranslationUnit} |
| 400 | 395 |
| 401 """ | 396 """ |
| 402 | 397 |
| 403 unit = self.UnitClass(source) | 398 unit = self.UnitClass(source) |
| 404 self.addunit(unit) | 399 self.addunit(unit) |
| 405 return unit | 400 return unit |
| 406 | 401 |
| 407 def findunit(self, source): | 402 def findunit(self, source): |
| 408 """Finds the unit with the given source string. | 403 """Finds the unit with the given source string. |
| 409 | 404 |
| 410 @rtype: L{TranslationUnit} or None | 405 @rtype: L{TranslationUnit} or None |
| 411 | 406 |
| 412 """ | 407 """ |
| 413 | 408 |
| 414 if len(getattr(self, "sourceindex", [])): | 409 if len(getattr(self, "sourceindex", [])): |
| 415 if source in self.sourceindex: | 410 if source in self.sourceindex: |
| 416 return self.sourceindex[source] | 411 return self.sourceindex[source] |
| 417 else: | 412 else: |
| 418 for unit in self.units: | 413 for unit in self.units: |
| 419 if unit.source == source: | 414 if unit.source == source: |
| 420 return unit | 415 return unit |
| 421 return None | 416 return None |
| 422 | 417 |
| 423 def translate(self, source): | 418 def translate(self, source): |
| 424 """Returns the translated string for a given source string. | 419 """Returns the translated string for a given source string. |
| 425 | 420 |
| 426 @rtype: String or None | 421 @rtype: String or None |
| 427 | 422 |
| 428 """ | 423 """ |
| 429 | 424 |
| 430 unit = self.findunit(source) | 425 unit = self.findunit(source) |
| 431 if unit and unit.target: | 426 if unit and unit.target: |
| 432 return unit.target | 427 return unit.target |
| 433 else: | 428 else: |
| 434 return None | 429 return None |
| 435 | 430 |
| 436 def makeindex(self): | 431 def makeindex(self): |
| 437 """Indexes the items in this store. At least .sourceindex should be usef ull.""" | 432 """Indexes the items in this store. At least .sourceindex should be usef ull.""" |
| 438 | 433 |
| 439 self.locationindex = {} | 434 self.locationindex = {} |
| 440 self.sourceindex = {} | 435 self.sourceindex = {} |
| 441 for unit in self.units: | 436 for unit in self.units: |
| 442 # Do we need to test if unit.source exists? | 437 # Do we need to test if unit.source exists? |
| 443 self.sourceindex[unit.source] = unit | 438 self.sourceindex[unit.source] = unit |
| 444 if unit.hasplural(): | 439 if unit.hasplural(): |
| 445 for nounform in unit.source.strings[1:]: | 440 for nounform in unit.source.strings[1:]: |
| 446 self.sourceindex[nounform] = unit | 441 self.sourceindex[nounform] = unit |
| 447 for location in unit.getlocations(): | 442 for location in unit.getlocations(): |
| 448 if location in self.locationindex: | 443 if location in self.locationindex: |
| 449 # if sources aren't unique, don't use them | 444 # if sources aren't unique, don't use them |
| 450 self.locationindex[location] = None | 445 self.locationindex[location] = None |
| 451 else: | 446 else: |
| 452 self.locationindex[location] = unit | 447 self.locationindex[location] = unit |
| 453 | 448 |
| 454 def __str__(self): | 449 def __str__(self): |
| 455 """Converts to a string representation that can be parsed back using L{p arsestring()}.""" | 450 """Converts to a string representation that can be parsed back using L{p arsestring()}.""" |
| 456 | 451 |
| 457 # We can't pickle fileobj if it is there, so let's hide it for a while. | 452 # We can't pickle fileobj if it is there, so let's hide it for a while. |
| 458 fileobj = getattr(self, "fileobj", None) | 453 fileobj = getattr(self, "fileobj", None) |
| 459 self.fileobj = None | 454 self.fileobj = None |
| 460 dump = pickle.dumps(self) | 455 dump = pickle.dumps(self) |
| 461 self.fileobj = fileobj | 456 self.fileobj = fileobj |
| 462 return dump | 457 return dump |
| 463 | 458 |
| 464 def isempty(self): | 459 def isempty(self): |
| 465 """Returns True if the object doesn't contain any translation units.""" | 460 """Returns True if the object doesn't contain any translation units.""" |
| 466 | 461 |
| 467 if len(self.units) == 0: | 462 if len(self.units) == 0: |
| 468 return True | 463 return True |
| 469 for unit in self.units: | 464 for unit in self.units: |
| 470 if not (unit.isblank() or unit.isheader()): | 465 if not (unit.isblank() or unit.isheader()): |
| 471 return False | 466 return False |
| 472 return True | 467 return True |
| 473 | 468 |
| 474 def _assignname(self): | 469 def _assignname(self): |
| 475 """Tries to work out what the name of the filesystem file is and | 470 """Tries to work out what the name of the filesystem file is and |
| 476 assigns it to .filename.""" | 471 assigns it to .filename.""" |
| 477 fileobj = getattr(self, "fileobj", None) | 472 fileobj = getattr(self, "fileobj", None) |
| 478 if fileobj: | 473 if fileobj: |
| 479 filename = getattr(fileobj, "name", getattr(fileobj, "filename", Non e)) | 474 filename = getattr(fileobj, "name", getattr(fileobj, "filename", Non e)) |
| 480 if filename: | 475 if filename: |
| 481 self.filename = filename | 476 self.filename = filename |
| 482 | 477 |
| 483 def parsestring(cls, storestring): | 478 def parsestring(cls, storestring): |
| 484 """Converts the string representation back to an object.""" | 479 """Converts the string representation back to an object.""" |
| 485 newstore = cls() | 480 newstore = cls() |
| 486 if storestring: | 481 if storestring: |
| 487 newstore.parse(storestring) | 482 newstore.parse(storestring) |
| 488 return newstore | 483 return newstore |
| 489 parsestring = classmethod(parsestring) | 484 parsestring = classmethod(parsestring) |
| 490 | 485 |
| 491 def parse(self, data): | 486 def parse(self, data): |
| 492 """parser to process the given source string""" | 487 """parser to process the given source string""" |
| 493 self.units = pickle.loads(data).units | 488 self.units = pickle.loads(data).units |
| 494 | 489 |
| 495 def savefile(self, storefile): | 490 def savefile(self, storefile): |
| 496 """Writes the string representation to the given file (or filename).""" | 491 """Writes the string representation to the given file (or filename).""" |
| 497 if isinstance(storefile, basestring): | 492 if isinstance(storefile, basestring): |
| 498 storefile = open(storefile, "w") | 493 storefile = open(storefile, "w") |
| 499 self.fileobj = storefile | 494 self.fileobj = storefile |
| 500 self._assignname() | 495 self._assignname() |
| 501 storestring = str(self) | 496 storestring = str(self) |
| 502 storefile.write(storestring) | 497 storefile.write(storestring) |
| 503 storefile.close() | 498 storefile.close() |
| 504 | 499 |
| 505 def save(self): | 500 def save(self): |
| 506 """Save to the file that data was originally read from, if available.""" | 501 """Save to the file that data was originally read from, if available.""" |
| 507 fileobj = getattr(self, "fileobj", None) | 502 fileobj = getattr(self, "fileobj", None) |
| 508 if not fileobj: | 503 if not fileobj: |
| 509 filename = getattr(self, "filename", None) | 504 filename = getattr(self, "filename", None) |
| 510 if filename: | 505 if filename: |
| 511 fileobj = file(filename, "w") | 506 fileobj = file(filename, "w") |
| 512 else: | 507 else: |
| 513 fileobj.close() | 508 fileobj.close() |
| 514 filename = getattr(fileobj, "name", getattr(fileobj, "filename", Non e)) | 509 filename = getattr(fileobj, "name", getattr(fileobj, "filename", Non e)) |
| 515 if not filename: | 510 if not filename: |
| 516 raise ValueError("No file or filename to save to") | 511 raise ValueError("No file or filename to save to") |
| 517 fileobj = fileobj.__class__(filename, "w") | 512 fileobj = fileobj.__class__(filename, "w") |
| 518 self.savefile(fileobj) | 513 self.savefile(fileobj) |
| 519 | 514 |
| 520 def parsefile(cls, storefile): | 515 def parsefile(cls, storefile): |
| 521 """Reads the given file (or opens the given filename) and parses back to an object.""" | 516 """Reads the given file (or opens the given filename) and parses back to an object.""" |
| 522 | 517 |
| 523 if isinstance(storefile, basestring): | 518 if isinstance(storefile, basestring): |
| 524 storefile = open(storefile, "r") | 519 storefile = open(storefile, "r") |
| 525 mode = getattr(storefile, "mode", "r") | 520 mode = getattr(storefile, "mode", "r") |
| 526 #For some reason GzipFile returns 1, so we have to test for that here | 521 #For some reason GzipFile returns 1, so we have to test for that here |
| 527 if mode == 1 or "r" in mode: | 522 if mode == 1 or "r" in mode: |
| 528 storestring = storefile.read() | 523 storestring = storefile.read() |
| 529 storefile.close() | 524 storefile.close() |
| 530 else: | 525 else: |
| 531 storestring = "" | 526 storestring = "" |
| 532 newstore = cls.parsestring(storestring) | 527 newstore = cls.parsestring(storestring) |
| 533 newstore.fileobj = storefile | 528 newstore.fileobj = storefile |
| 534 newstore._assignname() | 529 newstore._assignname() |
| 535 return newstore | 530 return newstore |
| 536 parsefile = classmethod(parsefile) | 531 parsefile = classmethod(parsefile) |
| 537 | 532 |
| LEFT | RIGHT |