2009-06-29 07:06:01 -05:00
|
|
|
# ------------------------------------------------------------------------------
|
2013-06-25 05:04:23 -05:00
|
|
|
# This file is part of Appy, a framework for building applications in the Python
|
|
|
|
# language. Copyright (C) 2007 Gaetan Delannay
|
2009-06-29 07:06:01 -05:00
|
|
|
|
2013-06-25 05:04:23 -05:00
|
|
|
# Appy 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 3 of the License, or (at your option) any later
|
|
|
|
# version.
|
2009-06-29 07:06:01 -05:00
|
|
|
|
2013-06-25 05:04:23 -05:00
|
|
|
# Appy 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.
|
2009-06-29 07:06:01 -05:00
|
|
|
|
2013-06-25 05:04:23 -05:00
|
|
|
# You should have received a copy of the GNU General Public License along with
|
|
|
|
# Appy. If not, see <http://www.gnu.org/licenses/>.
|
2009-06-29 07:06:01 -05:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
2013-02-06 10:15:01 -06:00
|
|
|
from xml.sax.saxutils import quoteattr
|
2009-06-29 07:06:01 -05:00
|
|
|
from appy.shared.xml_parser import XmlElement
|
|
|
|
from appy.pod.odf_parser import OdfEnvironment as ns
|
|
|
|
from appy.pod import PodError
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
class PodElement:
|
|
|
|
OD_TO_POD = {'p': 'Text', 'h': 'Title', 'section': 'Section',
|
|
|
|
'table': 'Table', 'table-row': 'Row', 'table-cell': 'Cell',
|
|
|
|
None: 'Expression'}
|
|
|
|
POD_ELEMS = ('text', 'title', 'section', 'table', 'row', 'cell')
|
2013-06-25 05:04:23 -05:00
|
|
|
# Elements for which the '-' operator can be applied.
|
|
|
|
MINUS_ELEMS = ('section', 'table')
|
2013-03-15 10:50:28 -05:00
|
|
|
@staticmethod
|
2009-06-29 07:06:01 -05:00
|
|
|
def create(elem):
|
2013-03-15 10:50:28 -05:00
|
|
|
'''Used to create any POD elem that has an equivalent OD element. Not
|
2009-06-29 07:06:01 -05:00
|
|
|
for creating expressions, for example.'''
|
|
|
|
return eval(PodElement.OD_TO_POD[elem])()
|
|
|
|
|
|
|
|
class Text(PodElement):
|
|
|
|
OD = XmlElement('p', nsUri=ns.NS_TEXT)
|
2013-06-25 05:04:23 -05:00
|
|
|
# When generating an error we may need to surround it with a given tag and
|
|
|
|
# sub-tags.
|
|
|
|
subTags = []
|
2009-06-29 07:06:01 -05:00
|
|
|
|
|
|
|
class Title(PodElement):
|
|
|
|
OD = XmlElement('h', nsUri=ns.NS_TEXT)
|
|
|
|
subTags = []
|
|
|
|
|
|
|
|
class Section(PodElement):
|
|
|
|
OD = XmlElement('section', nsUri=ns.NS_TEXT)
|
|
|
|
subTags = [Text.OD]
|
2013-06-25 05:04:23 -05:00
|
|
|
# When we must remove the Section element from a buffer, the deepest element
|
|
|
|
# to remove is the Section element itself.
|
|
|
|
DEEPEST_TO_REMOVE = OD
|
2009-06-29 07:06:01 -05:00
|
|
|
|
|
|
|
class Cell(PodElement):
|
|
|
|
OD = XmlElement('table-cell', nsUri=ns.NS_TABLE)
|
|
|
|
subTags = [Text.OD]
|
|
|
|
def __init__(self):
|
|
|
|
self.tableInfo = None # ~OdTable~
|
2011-02-18 08:58:59 -06:00
|
|
|
self.colIndex = None # The column index for this cell, within its table.
|
2009-06-29 07:06:01 -05:00
|
|
|
|
|
|
|
class Row(PodElement):
|
|
|
|
OD = XmlElement('table-row', nsUri=ns.NS_TABLE)
|
|
|
|
subTags = [Cell.OD, Text.OD]
|
|
|
|
|
|
|
|
class Table(PodElement):
|
|
|
|
OD = XmlElement('table', nsUri=ns.NS_TABLE)
|
|
|
|
subTags = [Row.OD, Cell.OD, Text.OD]
|
2013-06-25 05:04:23 -05:00
|
|
|
# When we must remove the Table element from a buffer, the deepest element
|
|
|
|
# to remove is the Cell (it can only be done for one-row, one-cell tables).
|
|
|
|
DEEPEST_TO_REMOVE = Cell.OD
|
2009-06-29 07:06:01 -05:00
|
|
|
def __init__(self):
|
|
|
|
self.tableInfo = None # ~OdTable~
|
|
|
|
|
|
|
|
class Expression(PodElement):
|
2013-06-25 05:04:23 -05:00
|
|
|
'''Represents a Python expression that is found in a pod or px.'''
|
2009-06-29 07:06:01 -05:00
|
|
|
OD = None
|
2013-06-25 05:04:23 -05:00
|
|
|
def __init__(self, pyExpr, pod):
|
2013-02-06 10:15:01 -06:00
|
|
|
# The Python expression
|
2013-03-19 16:06:47 -05:00
|
|
|
self.expr = pyExpr.strip()
|
2013-06-25 05:04:23 -05:00
|
|
|
self.pod = pod # True if I work for pod, False if I work for px.
|
2013-05-24 03:16:16 -05:00
|
|
|
# Must we, when evaluating the expression, escape XML special chars
|
|
|
|
# or not?
|
|
|
|
if self.expr.startswith(':'):
|
|
|
|
self.expr = self.expr[1:]
|
|
|
|
self.escapeXml = False
|
|
|
|
else:
|
|
|
|
self.escapeXml = True
|
2013-06-25 05:04:23 -05:00
|
|
|
if self.pod:
|
|
|
|
# pod-only: store here the expression's true result (before being
|
|
|
|
# converted to a string).
|
|
|
|
self.result = None
|
|
|
|
# pod-only: the following bool indicates if this Expression instance
|
|
|
|
# has already been evaluated or not. Expressions which are tied to
|
|
|
|
# attribute hooks are already evaluated when the tied hook is
|
|
|
|
# evaluated: this boolean prevents the expression from being
|
|
|
|
# evaluated twice.
|
|
|
|
self.evaluated = False
|
|
|
|
# self.result and self.evaluated are not used by PX, because they
|
|
|
|
# are not thread-safe.
|
2013-02-06 10:15:01 -06:00
|
|
|
|
2009-06-29 07:06:01 -05:00
|
|
|
def evaluate(self, context):
|
2013-02-06 10:15:01 -06:00
|
|
|
'''Evaluates the Python expression (self.expr) with a given
|
2013-03-22 06:52:24 -05:00
|
|
|
p_context, and returns the result. More precisely, it returns a
|
|
|
|
tuple (result, escapeXml). Boolean escapeXml indicates if XML chars
|
2013-05-24 03:16:16 -05:00
|
|
|
must be escaped or not.'''
|
|
|
|
escapeXml = self.escapeXml
|
2013-02-06 10:15:01 -06:00
|
|
|
# Evaluate the expression, or get it from self.result if it has already
|
|
|
|
# been computed.
|
2013-06-25 05:04:23 -05:00
|
|
|
if self.pod and self.evaluated:
|
2013-02-06 10:15:01 -06:00
|
|
|
res = self.result
|
|
|
|
# It can happen only once, to ask to evaluate an expression that
|
|
|
|
# was already evaluated (from the tied hook). We reset here the
|
|
|
|
# boolean "evaluated" to allow for the next evaluation, probably
|
|
|
|
# with another context.
|
|
|
|
self.evaluated = False
|
|
|
|
else:
|
|
|
|
# Evaluates the Python expression
|
2013-06-25 05:04:23 -05:00
|
|
|
res = eval(self.expr, context)
|
|
|
|
# pod-only: cache the expression result.
|
|
|
|
if self.pod: self.result = res
|
|
|
|
# Converts the expr result to a string that can be inserted in the
|
|
|
|
# pod/px result.
|
2013-03-22 06:52:24 -05:00
|
|
|
resultType = res.__class__.__name__
|
|
|
|
if resultType == 'NoneType':
|
2009-06-29 07:06:01 -05:00
|
|
|
res = u''
|
2013-03-22 06:52:24 -05:00
|
|
|
elif resultType == 'str':
|
|
|
|
res = res.decode('utf-8')
|
|
|
|
elif resultType == 'unicode':
|
|
|
|
pass # Don't perform any conversion, unicode is the target type.
|
|
|
|
elif resultType == 'Px':
|
|
|
|
# A PX that must be called within the current PX. Call it with the
|
|
|
|
# current context.
|
2013-06-25 05:04:23 -05:00
|
|
|
res = res(context, applyTemplate=False)
|
2013-05-24 03:16:16 -05:00
|
|
|
# Force escapeXml to False.
|
2013-03-22 06:52:24 -05:00
|
|
|
escapeXml = False
|
2009-06-29 07:06:01 -05:00
|
|
|
else:
|
|
|
|
res = unicode(res)
|
2013-03-22 06:52:24 -05:00
|
|
|
return res, escapeXml
|
2013-02-06 10:15:01 -06:00
|
|
|
|
|
|
|
class Attributes(PodElement):
|
|
|
|
'''Represents a bunch of XML attributes that will be dumped for a given tag
|
2013-06-25 05:04:23 -05:00
|
|
|
in the result. pod-only.'''
|
2013-02-06 10:15:01 -06:00
|
|
|
OD = None
|
|
|
|
floatTypes = ('int', 'long', 'float')
|
|
|
|
dateTypes = ('DateTime',)
|
|
|
|
|
|
|
|
def __init__(self, env):
|
|
|
|
self.attrs = {}
|
|
|
|
# Depending on the result of a tied expression, we will dump, for
|
|
|
|
# another tag, the series of attrs that this instance represents.
|
|
|
|
self.tiedExpression = None
|
|
|
|
# We will need the env to get the full names of attributes to dump.
|
|
|
|
self.env = env
|
|
|
|
|
|
|
|
def computeAttributes(self, expr):
|
|
|
|
'''p_expr has been evaluated: its result is in expr.result. Depending
|
|
|
|
on its type, we will dump the corresponding attributes in
|
|
|
|
self.attrs.'''
|
|
|
|
exprType = expr.result.__class__.__name__
|
|
|
|
tags = self.env.tags
|
|
|
|
attrs = self.attrs
|
|
|
|
if exprType in self.floatTypes:
|
|
|
|
attrs[tags['value-type']] = 'float'
|
|
|
|
attrs[tags['value']] = str(expr.result)
|
|
|
|
elif exprType in self.dateTypes:
|
|
|
|
attrs[tags['value-type']] = 'date'
|
|
|
|
attrs[tags['value']] = expr.result.strftime('%Y-%m-%d')
|
|
|
|
else:
|
|
|
|
attrs[tags['value-type']] = 'string'
|
|
|
|
|
|
|
|
def evaluate(self, context):
|
|
|
|
# Evaluate first the tied expression, in order to determine its type.
|
|
|
|
try:
|
|
|
|
self.tiedExpression.evaluate(context)
|
2013-06-25 05:04:23 -05:00
|
|
|
self.tiedExpression.evaluated = True
|
2013-02-06 10:15:01 -06:00
|
|
|
except Exception, e:
|
|
|
|
# Don't set "evaluated" to True. This way, when the buffer will
|
|
|
|
# evaluate the expression directly, we will really evaluate it, so
|
|
|
|
# the error will be dumped into the pod result.
|
|
|
|
pass
|
|
|
|
# Analyse the return type of the expression.
|
|
|
|
self.computeAttributes(self.tiedExpression)
|
|
|
|
# Now, self.attrs has been populated. Transform it into a string.
|
|
|
|
res = ''
|
|
|
|
for name, value in self.attrs.iteritems():
|
|
|
|
res += ' %s=%s' % (name, quoteattr(value))
|
|
|
|
return res
|
2009-06-29 07:06:01 -05:00
|
|
|
# ------------------------------------------------------------------------------
|