########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/AttributeInfo.py,v 1.30 2006/08/11 15:13:36 jkloth Exp $
"""
Classes that support validation and evaluation of attribute values in
XSLT instruction elements
Copyright 2003 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""
from Ft import TranslateMessage
import cStringIO, traceback
from Ft.Xml.XPath import Conversions
from Ft.Xml.XPath import RuntimeException
from Ft.Xml.XPath import parser
_xpath_parser = parser.new()
from Ft.Xml.Xslt import XsltException,XsltRuntimeException,Error
from Ft.Xml.Xslt import parser
_xpattern_parser = parser.new()
del parser
from Ft.Xml import XML_NAMESPACE,XMLNS_NAMESPACE,EMPTY_NAMESPACE
from Ft.Xml.Lib.XmlString import IsQName,SplitQName
from AttributeValueTemplate import AttributeValueTemplate
class AttributeInfo:
display = 'unknown'
def __init__(self, required=0, default=None, description=''):
self.required = required
self.default = default
self.description = description
return
def __str__(self):
return self.display
def prepare(self, element, value):
if value is None:
return self.default
return value
reprocess = prepare
def validate(self, validation):
return 1
class _ConstantValue:
def __init__(self, value):
self.value = value
def isConstant(self):
return 1
def evaluate(self, context):
return self.value
def __repr__(self):
return repr(self.value)
def __nonzero__(self):
return self.value is not None
class Choice(AttributeInfo):
def __init__(self, values, required=0, default=None, description=''):
AttributeInfo.__init__(self, required, default, description)
self.values = values
return
def prepare(self, element, value):
if value is None:
return self.default
if value not in self.values:
# check for a AttributeInfo instance
allowed = filter(lambda v, t=type(self): type(v) is t, self.values)
for info in allowed:
try:
allowed.prepare(element, value)
return value
except:
pass
# if we get here it is an error
raise XsltException(Error.INVALID_ATTR_CHOICE, value, str(self))
return value
reprocess = prepare
def __str__(self):
return ' | '.join(map(lambda v: '"' + v + '"', self.values))
class Avt:
def __str__(self):
return '{ %s }' % self.display
def prepare(self, element, value):
if value is None:
return _ConstantValue(self.reprocess(element, self.default))
elif '{' not in value and '}' not in value:
return _ConstantValue(self.reprocess(element, value))
try:
return AttributeValueTemplate(value, self, element)
except SyntaxError, error:
# an error from the XPath parser
raise XsltException(Error.INVALID_AVT, value,
element.baseUri, element.lineNumber,
element.columnNumber, str(error))
except XsltException, error:
raise XsltException(Error.INVALID_AVT, value,
element.baseUri, element.lineNumber,
element.columnNumber, error.args[0])
class ChoiceAvt(Avt, Choice):
def __str__(self):
return '{ %s }' % Choice.__str__(self)
class AnyAvt(Avt, AttributeInfo):
display = _('any avt')
class String(AttributeInfo):
display = _('string')
class StringAvt(Avt, String):
pass
class Char(AttributeInfo):
"""
A string value with a length of one
"""
display = _('char')
def prepare(self, element, value):
if value is None:
return self.default
if len(value) > 1:
raise XsltException(Error.INVALID_CHAR_ATTR, value)
return value
reprocess = prepare
class CharAvt(Avt, Char):
pass
class Number(AttributeInfo):
display = _('number')
def prepare(self, element, value):
if value is None:
return self.default
try:
return float(value or self.default)
except:
raise XsltException(Error.INVALID_NUMBER_ATTR, value)
reprocess = prepare
class NumberAvt(Avt, Number):
reprocess = Number.prepare
class UriReference(AttributeInfo):
display = _('uri-reference')
def __init__(self, required=0, default=None, description='', isNsName=0):
AttributeInfo.__init__(self, required, default, description)
self._isNsName = isNsName
def prepare(self, element, value):
if value is None:
return self.default
if self._isNsName and \
value == XML_NAMESPACE or value == XMLNS_NAMESPACE:
raise XsltException(Error.INVALID_NS_URIREF_ATTR, value)
return value
reprocess = prepare
class UriReferenceAvt(Avt, UriReference):
pass
class Id(AttributeInfo):
display = _('id')
def prepare(self, element, value):
if value is None:
return self.default
if not value:
raise XsltException(Error.INVALID_ID_ATTR, value)
return value
reprocess = prepare
class IdAvt(Avt, Id):
pass
class QName(AttributeInfo):
display = _('qname')
def prepare(self, element, value):
if value is None:
if self.default is None:
return None
value = self.default
elif not IsQName(value):
raise XsltException(Error.INVALID_QNAME_ATTR, value)
prefix, local = SplitQName(value)
if prefix:
try:
namespace = element.namespaces[prefix]
except KeyError:
raise XsltRuntimeException(Error.UNDEFINED_PREFIX,
element, prefix)
else:
namespace = EMPTY_NAMESPACE
return (namespace, local)
reprocess = prepare
class QNameAvt(Avt, QName):
pass
class RawQName(QName):
def prepare(self, element, value):
if value is None:
if self.default is None:
return None
value = self.default
elif not IsQName(value):
raise XsltException(Error.INVALID_QNAME_ATTR, value)
return SplitQName(value)
reprocess = prepare
class RawQNameAvt(Avt, RawQName):
pass
class NCName(AttributeInfo):
display = _('ncname')
def prepare(self, element, value):
if value is None:
return self.default
if not value:
raise XsltException(Error.INVALID_NCNAME_ATTR, value)
if ':' in value:
raise XsltException(Error.INVALID_NCNAME_ATTR, value)
return value
reprocess = prepare
class NCNameAvt(Avt, NCName):
pass
class Prefix(AttributeInfo):
display = _('prefix')
def prepare(self, element, value):
if value is None:
return self.default
if not value:
raise XsltException(Error.INVALID_PREFIX_ATTR, value)
if ':' in value:
raise XsltException(Error.INVALID_PREFIX_ATTR, value)
if value == '#default':
value = None
return value
reprocess = prepare
class PrefixAvt(Avt, Prefix):
pass
class NMToken(AttributeInfo):
display = _('nmtoken')
def prepare(self, element, value):
if value is None:
return self.default
if not value:
raise XsltException(Error.INVALID_NMTOKEN_ATTR, value)
return value
reprocess = prepare
class NMTokenAvt(Avt, NMToken):
pass
class QNameButNotNCName(AttributeInfo):
display = _('qname-but-not-ncname')
def prepare(self, element, value):
if value is None:
if self.default is None:
return None
value = self.default
elif not value:
raise XsltException(Error.QNAME_BUT_NOT_NCNAME, value)
try:
index = value.index(':')
except ValueError:
raise XsltException(Error.QNAME_BUT_NOT_NCNAME, value)
prefix, local = value[:index], value[index+1:]
try:
namespace = element.namespaces[prefix]
except KeyError:
raise XsltRuntimeException(Error.UNDEFINED_PREFIX,
element, prefix)
return (namespace, local)
reprocess = prepare
class Token(AttributeInfo):
"""
An attribute whose value is used as an XPath NameTest
"""
display = _('token')
def prepare(self, element, value):
# a 'token' is really an XPath NameTest; '*' | NCName ':' '*' | QName
# From XPath 1.0 section 2.3:
# if the QName does not have a prefix, then the namespace URI is null
index = value.rfind(':')
if index == -1:
namespace = None
local = value
else:
prefix = value[:index]
local = value[index+1:]
try:
namespace = element.namespaces[prefix]
except KeyError:
raise XsltRuntimeException(Error.UNDEFINED_PREFIX,
element, prefix)
return (namespace, local)
reprocess = prepare
class TokenAvt(Avt, Token):
pass
class ExpressionWrapper:
def __init__(self, expression, element, original):
self.expression = expression
self.element = element
self.original = original
return
def __nonzero__(self):
# True if self.expression is not None, which is always the case
# otherwise this instance would not exist!
return True
def __getattr__(self, attr):
"""Make this behave as if it was the expression object itself."""
return getattr(self.expression, attr)
# Provide the copy/pickle helpers so as to not get them from the
# wrapped expression.
def __getstate__(self):
return (self.expression, self.element, self.original)
def __setstate__(self, state):
self.expression, self.element, self.original = state
return
def evaluate(self,context):
try:
return self.expression.evaluate(context)
except XPathRuntimeException, e:
import MessageSource
e.message = MessageSource.EXPRESSION_POSITION_INFO % (
self.element.baseUri, self.element.lineNumber,
self.element.columnNumber, self.original, str(e))
# By modifying the exception value directly, we do not need
# to raise with that value, thus leaving the frame stack
# intact (original traceback is displayed).
raise
except XsltRuntimeException, e:
import MessageSource
e.message = MessageSource.XSLT_EXPRESSION_POSITION_INFO % (
str(e), self.original)
# By modifying the exception value directly, we do not need
# to raise with that value, thus leaving the frame stack
# intact (original traceback is displayed).
raise
except (KeyboardInterrupt, SystemExit):
raise
except Exception, e:
import MessageSource
tb = cStringIO.StringIO()
tb.write("Lower-level traceback:\n")
traceback.print_exc(1000, tb)
raise RuntimeError(MessageSource.EXPRESSION_POSITION_INFO % (
self.element.baseUri, self.element.lineNumber,
self.element.columnNumber, self.original, tb.getvalue()))
class Expression(AttributeInfo):
"""
An attribute whose value is used as an XPath expression
"""
display = _('expression')
def prepare(self, element, value):
if value is None:
if self.default is None:
return None
value = self.default
try:
expression = _xpath_parser.parse(value)
except SyntaxError, error:
raise XsltException(Error.INVALID_EXPRESSION, value,
element.baseUri, element.lineNumber,
element.columnNumber, str(error))
return ExpressionWrapper(expression, element, value)
class NodeSetExpression(Expression):
display = _('node-set-expression')
class StringExpression(Expression):
display = _('string-expression')
class NumberExpression(Expression):
display = _('number-expression')
class BooleanExpression(Expression):
display = _('boolean-expression')
class Pattern(AttributeInfo):
"""
An attribute whose value is used as an XPattern expression
"""
display = _('pattern')
def prepare(self, element, value):
if value is None:
if self.default:
value = self.default
else:
return None
try:
return _xpattern_parser.parse(value)
except SyntaxError, error:
raise XsltException(Error.INVALID_PATTERN, value,
element.baseUri, element.lineNumber,
element.columnNumber, str(error))
class Tokens(Token):
"""
A whitespace separated list of tokens (see Token for description of a token)
"""
display = _('tokens')
def prepare(self, element, value):
if value is None:
return []
tokens = []
for token in value.split():
prepared = Token.prepare(self, element, token)
tokens.append(prepared)
return tokens
reprocess = prepare
class TokensAvt(Avt, Tokens):
pass
class QNames(QName):
"""
A whitespace separated list of qnames (see QName for description of a qname)
"""
display = _('qnames')
def prepare(self, element, value):
if value is None:
return []
qnames = []
for qname in value.split():
prepared = QName.prepare(self, element, qname)
qnames.append(prepared)
return qnames
reprocess = prepare
class QNamesAvt(Avt, QNames):
pass
class Prefixes(Prefix):
"""
A whitespace separated list of prefixes (see Prefix for more information)
"""
display = _('prefixes')
def prepare(self, element, value):
if value is None:
return []
prefixes = []
for prefix in value.split():
prepared = Prefix.prepare(self, element, prefix)
prefixes.append(prepared)
return prefixes
reprocess = prepare
class PrefixesAvt(Avt, Prefixes):
pass
class YesNo(AttributeInfo):
display = '"yes" | "no"'
def prepare(self, element, value):
if value is None:
return self.default and self.default == 'yes'
elif value not in ['yes', 'no']:
raise XsltException(Error.INVALID_ATTR_CHOICE, value, str(self))
return value == 'yes'
reprocess = prepare
class YesNoAvt(Avt, YesNo):
pass
|