Viewing file: StylesheetTree.py (9.3 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/StylesheetTree.py,v 1.22 2005/04/06 23:05:47 jkloth Exp $ """ Node classes for the stylesheet tree
Copyright 2004 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """ from Ft.Xml import EMPTY_NAMESPACE from Ft.Xml.Domlette import GetAllNs from Ft.Xml.Xslt import XSL_NAMESPACE, XsltException, XsltRuntimeException, Error from Ft.Xml.Xslt import AttributeValueTemplate from Ft.Xml.Xslt import CategoryTypes from Ft.Xml.Xslt import ContentInfo
from Ft.Xml.XPath import parser _xpath_parser = parser from Ft.Xml.Xslt import parser _xpattern_parser = parser del parser
class XsltNode:
# positional information (for debugger and errors) # baseUri also used to set base URI of rtf nodes baseUri = '' lineNumber = '??' columnNumber = '??' importIndex = -1
root = None parent = None expandedName = (None, None) nodeName = None children = None attributes = None
# hints for performance doesSetup = False doesPrime = False doesIdle = False
def isLastChild(self): siblings = self.parent.children if siblings.index(self) == len(siblings) - 1: return 1 else: isLast = 1 for node in siblings[siblings.index(self)+1:]: if not node.isPseudoNode(): isLast = 0 break return isLast
def setup(self): return
def prime(self, processor, context): return
def idle(self, processor): return
def instantiate(self, context, processor): return
def isPseudoNode(self): return False
def pprint(self, _indent=''): print _indent + str(self) if self.children: _indent += ' ' for child in self.children: child.pprint(_indent) return
class XsltRoot(XsltNode):
content = ContentInfo.Alt(ContentInfo.QName(XSL_NAMESPACE, 'xsl:stylesheet'), ContentInfo.QName(XSL_NAMESPACE, 'xsl:transform'), ContentInfo.ResultElements)
validator = ContentInfo.Validator(content)
nodeName = u'#document'
def __init__(self, baseUri): self.root = self self.baseUri = baseUri # cache of original stylesheet source docs, keyed by URI self.sources = {} # XML strings self.sourceNodes = {} # Domlette documents # Regarding the cache: # # XSLT's document() function requires that the original stylesheet # documents be accessible as separate source document trees, but the # point of StylesheetReader is to avoid creating a Domlette document # for each stylesheet while building a single, compact stylesheet tree # (using the classes in this module) via the fastest parsing method # available. In order to support document(), we have to make it # possible to create, as needed, a Domlette document for each original # stylesheet, just in case somewhere in the stylesheet tree a # document() call or extension function makes reference to one of # those original documents. document('') is the most common case, but # not the only one. # # For now, we cache the original bytes of the document entity in the # stylesheet tree's XsltRoot object, and implement document() such # that it looks for documents in the Domlette cache in the XsltContext # first, then tries the cache we set up here, before falling back on # the normal resolver to fetch fresh content. If the doc is found in # our cache, then it is parsed and the Domlette is stored in the # XsltContext for future access. See XsltFunctions.py. # # Future optimization ideas: # - After the stylesheet tree is built, look for document() calls # in the XPath expressions, and if they all use single arguments # that are hard-coded URI refs, resolve the refs to absolute form, # and discard any cached docs that aren't in the resulting list. # - Compress the cached strings, and decompress on the fly. # # FIXME: The cache does not include external entities or XIncludes; # they are refetched each time the cached document entity is read. # It is rare for a stylesheet to use ext. entities/XIncludes, though. self.primeInstructions = [] self.idleInstructions = [] self.stylesheet = None self.children = [] return
def appendChild(self, child): # The validator ensures that only one child will be added child.parent = self self.stylesheet = child self.children = [child] return
def __str__(self): return "<XsltRoot at 0x%x>" % id(self)
# implements default behaviour for extension elements class XsltElement(XsltNode):
category = CategoryTypes.RESULT_ELEMENT content = ContentInfo.Template validator = ContentInfo.Validator(content) legalAttrs = None # this means no error checking or defaulting
def __init__(self, root, namespaceUri, localName, baseUri): self.root = root self.baseUri = baseUri self.expandedName = (namespaceUri, localName) self.children = [] self.attributes = {} self.namespaces = {} return
def insertChild(self, index, child): self.children.insert(index, child) child.parent = self if child.doesSetup: child.setup() return
def appendChild(self, child): self.children.append(child) child.parent = self if child.doesSetup: child.setup() return
def parseAVT(self, avt): if avt is None: return None try: return AttributeValueTemplate.AttributeValueTemplate(avt) except SyntaxError, error: raise XsltException(Error.INVALID_AVT, avt, self.baseUri, self.lineNumber, self.columnNumber, str(error)) except XsltException, error: raise XsltException(Error.INVALID_AVT, avt, self.baseUri, self.lineNumber, self.columnNumber, error.args[0])
def parseExpression(self, expression): if expression is None: return None p = _xpath_parser.new() try: return p.parse(expression) except SyntaxError, error: raise XsltException(Error.INVALID_EXPRESSION, expression, self.baseUri, self.lineNumber, self.columnNumber, str(error))
def parsePattern(self, pattern): if pattern is None: return None p = _xpattern_parser.new() try: return p.parse(pattern) except SyntaxError, error: raise XsltException(Error.INVALID_PATTERN, pattern, self.baseUri, self.lineNumber, self.columnNumber, str(error))
def splitQName(self, qname): if not qname: return None index = qname.find(':') if index != -1: split = (qname[:index], qname[index+1:]) else: split = (None, qname) return split
def expandQName(self, qname, refNode=None): if not qname: return None if refNode: namespaces = GetAllNs(refNode) else: namespaces = self.namespaces prefix, local = self.splitQName(qname) if prefix: try: expanded = (namespaces[prefix], local) except KeyError: raise XsltRuntimeException(Error.UNDEFINED_PREFIX, self, prefix) else: expanded = (EMPTY_NAMESPACE, local) return expanded
def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self
for child in self.children: child.instantiate(context, processor) return
def processChildren(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self
for child in self.children: child.instantiate(context, processor) return
def __str__(self): #FIXME: Should this use self.__class__ or sth rather than hardcoding "XsltElement"? return ("<XsltElement at 0x%x:" " name %r, %d attributes, %d children, precedence %d>") % ( id(self), self.nodeName, len(self.attributes), len(self.children), self.importIndex)
class XsltText(XsltNode):
nodeName = u'#text'
def __init__(self, root, baseUri, data): self.root = root self.baseUri = baseUri self.data = data return
def instantiate(self, context, processor): processor.writers[-1].text(self.data) return
def __str__(self): if len(self.data) > 20: data = self.data[:20] + '...' else: data = self.data return "<XsltText at 0x%x: %s>" % (id(self), repr(data))
|