230 lines
11 KiB
Python
230 lines
11 KiB
Python
# ------------------------------------------------------------------------------
|
|
# Appy is a framework for building applications in the Python language.
|
|
# Copyright (C) 2007 Gaetan Delannay
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
|
|
|
# ------------------------------------------------------------------------------
|
|
from appy.pod import PodError
|
|
from appy.pod.elements import *
|
|
|
|
# ------------------------------------------------------------------------------
|
|
EVAL_ERROR = 'Error while evaluating expression "%s".'
|
|
FROM_EVAL_ERROR = 'Error while evaluating the expression "%s" defined in the ' \
|
|
'"from" part of a statement.'
|
|
WRONG_SEQ_TYPE = 'Expression "%s" is not iterable.'
|
|
TABLE_NOT_ONE_CELL = "The table you wanted to populate with '%s' " \
|
|
"can\'t be dumped with the '-' option because it has " \
|
|
"more than one cell in it."
|
|
|
|
# ------------------------------------------------------------------------------
|
|
class BufferAction:
|
|
'''Abstract class representing a action (=statement) that must be performed
|
|
on the content of a buffer (if, for...).'''
|
|
def __init__(self, name, buffer, expr, elem, minus, source, fromExpr):
|
|
self.name = name # Actions may be named. Currently, the name of an
|
|
# action is only used for giving a name to "if" actions; thanks to this
|
|
# name, "else" actions that are far away may reference their "if".
|
|
self.buffer = buffer # The object of the action
|
|
self.expr = expr # Python expression to evaluate (may be None in the
|
|
# case of a NullAction or ElseAction, for example)
|
|
self.elem = elem # The element within the buffer that is the object
|
|
# of the action.
|
|
self.minus = minus # If True, the main buffer element(s) must not be
|
|
# dumped.
|
|
self.result = self.buffer.getFileBuffer()
|
|
self.source = source # if 'buffer', we must dump the (evaluated) buffer
|
|
# content. If 'from', we must dump what comes from the 'from' part of
|
|
# the action (='fromExpr')
|
|
self.fromExpr = fromExpr
|
|
# We store the result of evaluation of expr and fromExpr
|
|
self.exprResult = None
|
|
self.fromExprResult = None
|
|
def writeError(self, errorMessage, dumpTb=True):
|
|
# Empty the buffer
|
|
self.buffer.__init__(self.buffer.env, self.buffer.parent)
|
|
PodError.dump(self.buffer, errorMessage, withinElement=self.elem,
|
|
dumpTb=dumpTb)
|
|
self.buffer.evaluate()
|
|
def execute(self):
|
|
# Check that if minus is set, we have an element which can accept it
|
|
if self.minus and isinstance(self.elem, Table) and \
|
|
(not self.elem.tableInfo.isOneCell()):
|
|
self.writeError(TABLE_NOT_ONE_CELL % self.expr)
|
|
else:
|
|
errorOccurred = False
|
|
if self.expr:
|
|
try:
|
|
self.exprResult = eval(self.expr, self.buffer.env.context)
|
|
except:
|
|
self.exprResult = None
|
|
self.writeError(EVAL_ERROR % self.expr)
|
|
errorOccurred = True
|
|
if not errorOccurred:
|
|
self.do()
|
|
def evaluateBuffer(self):
|
|
if self.source == 'buffer':
|
|
self.buffer.evaluate(removeMainElems = self.minus)
|
|
else:
|
|
# Evaluate fromExpr
|
|
self.fromExprResult = None
|
|
errorOccurred = False
|
|
try:
|
|
self.fromExprResult= eval(self.fromExpr,self.buffer.env.context)
|
|
except PodError, pe:
|
|
self.writeError(FROM_EVAL_ERROR % self.fromExpr + ' ' + str(pe),
|
|
dumpTb=False)
|
|
errorOccurred = True
|
|
except:
|
|
self.writeError(FROM_EVAL_ERROR % self.fromExpr)
|
|
errorOccurred = True
|
|
if not errorOccurred:
|
|
self.result.write(self.fromExprResult)
|
|
|
|
class IfAction(BufferAction):
|
|
'''Action that determines if we must include the content of the buffer in
|
|
the result or not.'''
|
|
def do(self):
|
|
if self.exprResult:
|
|
self.evaluateBuffer()
|
|
else:
|
|
if self.buffer.isMainElement(Cell.OD):
|
|
# Don't leave the current row with a wrong number of cells
|
|
self.result.dumpElement(Cell.OD.elem)
|
|
|
|
class ElseAction(IfAction):
|
|
'''Action that is linked to a previous "if" action. In fact, an "else"
|
|
action works exactly like an "if" action, excepted that instead of
|
|
defining a conditional expression, it is based on the negation of the
|
|
conditional expression of the last defined "if" action.'''
|
|
def __init__(self, name, buffer, expr, elem, minus, source, fromExpr,
|
|
ifAction):
|
|
IfAction.__init__(self, name, buffer, None, elem, minus, source,
|
|
fromExpr)
|
|
self.ifAction = ifAction
|
|
def do(self):
|
|
# The result of this "else" action is "not <result from last execution
|
|
# of linked 'if' action>".
|
|
self.exprResult = not self.ifAction.exprResult
|
|
IfAction.do(self)
|
|
|
|
class ForAction(BufferAction):
|
|
'''Actions that will include the content of the buffer as many times as
|
|
specified by the action parameters.'''
|
|
def __init__(self, name, buffer, expr, elem, minus, iter, source, fromExpr):
|
|
BufferAction.__init__(self, name, buffer, expr, elem, minus, source,
|
|
fromExpr)
|
|
self.iter = iter # Name of the iterator variable used in the each loop
|
|
def do(self):
|
|
context = self.buffer.env.context
|
|
# Check self.exprResult type
|
|
try:
|
|
iter(self.exprResult)
|
|
# All "iterable" objects are OK. Thanks to Bernhard Bender for this
|
|
# improvement.
|
|
except TypeError:
|
|
self.writeError(WRONG_SEQ_TYPE % self.expr)
|
|
return
|
|
# Remember variable hidden by iter if any
|
|
hasHiddenVariable = False
|
|
if context.has_key(self.iter):
|
|
hiddenVariable = context[self.iter]
|
|
hasHiddenVariable = True
|
|
# In the case of cells, initialize some values
|
|
isCell = False
|
|
if isinstance(self.elem, Cell):
|
|
isCell = True
|
|
nbOfColumns = self.elem.tableInfo.nbOfColumns
|
|
initialColIndex = self.elem.tableInfo.curColIndex
|
|
currentColIndex = initialColIndex
|
|
rowAttributes = self.elem.tableInfo.curRowAttrs
|
|
# If self.exprResult is empty, dump an empty cell to avoid
|
|
# having the wrong number of cells for the current row
|
|
if not self.exprResult:
|
|
self.result.dumpElement(Cell.OD.elem)
|
|
# Enter the "for" loop
|
|
for item in self.exprResult:
|
|
context[self.iter] = item
|
|
# Cell: add a new row if we are at the end of a row
|
|
if isCell and (currentColIndex == nbOfColumns):
|
|
self.result.dumpEndElement(Row.OD.elem)
|
|
self.result.dumpStartElement(Row.OD.elem, rowAttributes)
|
|
currentColIndex = 0
|
|
self.evaluateBuffer()
|
|
# Cell: increment the current column index
|
|
if isCell:
|
|
currentColIndex += 1
|
|
# Cell: leave the last row with the correct number of cells
|
|
if isCell and self.exprResult:
|
|
wrongNbOfCells = (currentColIndex-1) - initialColIndex
|
|
if wrongNbOfCells < 0: # Too few cells for last row
|
|
for i in range(abs(wrongNbOfCells)):
|
|
context[self.iter] = ''
|
|
self.buffer.evaluate(subElements=False)
|
|
# This way, the cell is dumped with the correct styles
|
|
elif wrongNbOfCells > 0: # Too many cells for last row
|
|
# Finish current row
|
|
nbOfMissingCells = 0
|
|
if currentColIndex < nbOfColumns:
|
|
nbOfMissingCells = nbOfColumns - currentColIndex
|
|
context[self.iter] = ''
|
|
for i in range(nbOfMissingCells):
|
|
self.buffer.evaluate(subElements=False)
|
|
self.result.dumpEndElement(Row.OD.elem)
|
|
# Create additional row with remaining cells
|
|
self.result.dumpStartElement(Row.OD.elem, rowAttributes)
|
|
nbOfRemainingCells = wrongNbOfCells + nbOfMissingCells
|
|
nbOfMissingCellsLastLine = nbOfColumns - nbOfRemainingCells
|
|
context[self.iter] = ''
|
|
for i in range(nbOfMissingCellsLastLine):
|
|
self.buffer.evaluate(subElements=False)
|
|
# Restore hidden variable if any
|
|
if hasHiddenVariable:
|
|
context[self.iter] = hiddenVariable
|
|
else:
|
|
if self.exprResult:
|
|
del context[self.iter]
|
|
|
|
class NullAction(BufferAction):
|
|
'''Action that does nothing. Used in conjunction with a "from" clause, it
|
|
allows to insert in a buffer arbitrary odt content.'''
|
|
def do(self):
|
|
self.evaluateBuffer()
|
|
|
|
class VariableAction(BufferAction):
|
|
'''Action that allows to define a variable somewhere in the template.'''
|
|
def __init__(self, name, buffer, expr, elem, minus, varName, source,
|
|
fromExpr):
|
|
BufferAction.__init__(self, name, buffer, expr, elem, minus, source,
|
|
fromExpr)
|
|
self.varName = varName # Name of the variable
|
|
def do(self):
|
|
context = self.buffer.env.context
|
|
# Remember the variable hidden by our variable definition, if any
|
|
hasHiddenVariable = False
|
|
if context.has_key(self.varName):
|
|
hiddenVariable = context[self.varName]
|
|
hasHiddenVariable = True
|
|
# Add the variable to the context
|
|
context[self.varName] = self.exprResult
|
|
# Evaluate the buffer
|
|
self.evaluateBuffer()
|
|
# Restore hidden variable if any
|
|
if hasHiddenVariable:
|
|
context[self.varName] = hiddenVariable
|
|
else:
|
|
del context[self.varName]
|
|
# ------------------------------------------------------------------------------
|