[4suite-checkins] In 4Suite/Ft/Xml/Xslt, files ApplyTemplatesElement.py, CallTemplateElement.py, ChooseElement.py, ContentInfo.py, ForEachElement.py, GenericVariable.py, LiteralElement.py, Processor.py, Stylesheet.py, StylesheetHandler.py, StylesheetReader.py, StylesheetTree.py, TemplateElement.py, TextElement.py, UndefinedElements.py, ValueOfElement.py, XPathExtensions.py, XsltContext.py, XsltFunctions.py

Jeremy Kloth jkloth at 4suite.org
Mon Nov 27 09:29:30 MST 2006


Modified Files:
    ApplyTemplatesElement.py CallTemplateElement.py ChooseElement.py
    ContentInfo.py ForEachElement.py GenericVariable.py
    LiteralElement.py Processor.py Stylesheet.py StylesheetHandler.py
    StylesheetReader.py StylesheetTree.py TemplateElement.py
    TextElement.py UndefinedElements.py ValueOfElement.py
    XPathExtensions.py XsltContext.py XsltFunctions.py

Log Message:
- Xslt{Node,Root,Text,Element} are now C-types; this reduces memory footprint
  add paves the way for a C-based StylesheetReader.
- ContentInfo classes replace by Domlette's ContentModel; this should be
  transparent to implementors
- StylesheetHandler streamlined; only visible change is now CategoryTypes
  (i.e., .category on XsltElement subclasses) is ignored for validation
  purposes.
- Miscellaneous other speedups

ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ApplyTemplatesElement.py.diff?r1=1.10&r2=1.11
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ApplyTemplatesElement.py?rev=1.11&content-type=text/vnd.viewcvs-markup

Index: ApplyTemplatesElement.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/ApplyTemplatesElement.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -U2 -r1.10 -r1.11
--- ApplyTemplatesElement.py	6 Apr 2005 23:05:47 -0000	1.10
+++ ApplyTemplatesElement.py	27 Nov 2006 16:29:29 -0000	1.11
@@ -26,5 +26,5 @@
         }
 
-    doesSetup = 1
+    doesSetup = True
 
     def setup(self):
@@ -68,6 +68,5 @@
 
         # Iterate over the nodes
-        state = context.copy()
-        mode = context.mode
+        state = context.node, context.position, context.size, context.mode
         context.mode = self._instantiate_mode(context)
 
@@ -78,6 +77,4 @@
             processor.applyTemplates(context, with_params)
             pos += 1
-
-        context.mode = mode
-        context.set(state)
+        context.node, context.position, context.size, context.mode = state
         return
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/CallTemplateElement.py.diff?r1=1.11&r2=1.12
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/CallTemplateElement.py?rev=1.12&content-type=text/vnd.viewcvs-markup

Index: CallTemplateElement.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/CallTemplateElement.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -U2 -r1.11 -r1.12
--- CallTemplateElement.py	6 Apr 2005 23:05:47 -0000	1.11
+++ CallTemplateElement.py	27 Nov 2006 16:29:29 -0000	1.12
@@ -31,9 +31,8 @@
         self._tail_recursive = 0
         self._called_template = None
-        self._params = map(lambda with_param:
-                           (with_param, with_param._name, with_param._select),
-                           self.children)
+        self._params = [ (child, child._name, child._select.evaluate)
+                         for child in self.children ]
         return
-        
+
     def prime(self, processor, context):
         self._called_template = processor._namedTemplates.get(self._name)
@@ -51,7 +50,7 @@
                     while node is not current:
                         if not (node.isLastChild() and \
-                                (node.expandedName[0] == XSL_NAMESPACE and 
-                                 node.expandedName[1] in ['choose', 'if',
-                                                          'otherwise', 'when'])
+                                (node.expandedName[0] == XSL_NAMESPACE and
+                                 node.expandedName[1] in ('choose', 'if',
+                                                          'otherwise', 'when'))
                                 ):
                             use_tail = 0
@@ -81,5 +80,5 @@
             context.processorNss = param.namespaces
             context.currentInstruction = param
-            params[name] = expr.evaluate(context)
+            params[name] = expr(context)
 
         if self._tail_recursive:
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ChooseElement.py.diff?r1=1.4&r2=1.5
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ChooseElement.py?rev=1.5&content-type=text/vnd.viewcvs-markup

Index: ChooseElement.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/ChooseElement.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -U2 -r1.4 -r1.5
--- ChooseElement.py	6 Apr 2005 23:05:47 -0000	1.4
+++ ChooseElement.py	27 Nov 2006 16:29:29 -0000	1.5
@@ -46,19 +46,25 @@
         if not self.children:
             raise XsltException(Error.CHOOSE_REQUIRES_WHEN)
+        elif isinstance(self.children[-1], OtherwiseElement):
+            self._choices = self.children[:-1]
+            self._otherwise = self.children[-1]
+        else:
+            self._choices = self.children
+            self._otherwise = None
+        self._choices = [ (child, child._test.evaluate)
+                          for child in self._choices ]
         return
 
     def instantiate(self, context, processor):
 
-        chosen = None
-        for child in self.children:
+        for child, test in self._choices:
             context.processorNss = child.namespaces
             context.currentInstruction = child
-            if isinstance(child, WhenElement):
-                if Conversions.BooleanValue(child._test.evaluate(context)):
-                    chosen = child
-                    break
-            else:
-                # xsl:otherwise
+            if Conversions.BooleanValue(test(context)):
                 chosen = child
+                break
+        else:
+            # xsl:otherwise
+            chosen = self._otherwise
 
         if chosen:
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ContentInfo.py.diff?r1=1.8&r2=1.9
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ContentInfo.py?rev=1.9&content-type=text/vnd.viewcvs-markup

Index: ContentInfo.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/ContentInfo.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -U2 -r1.8 -r1.9
--- ContentInfo.py	16 Jan 2005 07:34:39 -0000	1.8
+++ ContentInfo.py	27 Nov 2006 16:29:29 -0000	1.9
@@ -1,234 +1,132 @@
-import sys
+from Ft.Xml.cDomlettec import ContentModel
+from Ft.Xml.Lib.XmlString import IsQName
 from Ft.Xml.Xslt import XSL_NAMESPACE
-from Ft.Xml.Xslt import CategoryTypes
 
-TEXT_NODE = (None, None)
-EMPTY = -sys.maxint
-ELSE = EMPTY + 1
+__all__ = ['QName', 'Seq', 'Alt', 'Rep1', 'Rep', 'Opt',
+           'Empty', 'Text', 'ResultElements', 'Instructions', 'Template',
+           'TopLevelElements',
+           ]
 
-class Atom:
+RESULT_ELEMENT = (None, None)
+TEXT_NODE = '#PCDATA'
+EMPTY = '/empty/'
+END_ELEMENT = ContentModel.FINAL_EVENT
+
+def QName(namespaceUri, qualifiedName):
     """
-    Atom(arg)
-    matches exactly one occurence of 'arg'
+    Matches a fully qualified name (e.g., xsl:sort)
     """
-    def __init__(self, token):
-        self.token = token
+    assert IsQName(qualifiedName)
+    if ':' in qualifiedName:
+        localName = qualifiedName[qualifiedName.index(':')+1:]
+    else:
+        localName = qualifiedName
+    return ContentModel(ContentModel.TYPE_NAME, (namespaceUri, localName),
+                        label=qualifiedName)
 
-    def buildValidator(self, validator, initial, final):
-        validator.transition(self.token, initial, final)
-        return
-    
 
-class Seq:
+def Seq(*args):
     """
-    Seq(arg1, arg2, arg3...)
-    matches arg1 followed by arg2 followed by arg3...
+    Matches the each argument in sequential order.
     """
-    def __init__(self, *args):
-        self.args = []
-        for arg in args:
-            if isinstance(arg, type('')):
-                arg = QName(arg)
-            self.args.append(arg)
-        return
-
-    def __str__(self):
-        if len(self.args) > 1:
-            return '(%s)' % ', '.join(map(str, self.args))
-        else:
-            return str(self.args[0])
-
-    def buildValidator(self, validator, initial, final):
-        s1 = initial
-        for arg in self.args[:-1]:
-            s2 = {}
-            arg.buildValidator(validator, s1, s2)
-            s1 = s2
-        self.args[-1].buildValidator(validator, s1, final)
-        return
+    return ContentModel(ContentModel.TYPE_SEQ, args)
 
 
-class Alt:
+def Alt(*args):
     """
-    Alt(arg1, arg2, arg3...)
-    matches arg1 or arg2 or arg3...
+    Matches one of the given arguments.
     """
-
-    def __init__(self, *args):
-        self.args = []
-        for arg in args:
-            if isinstance(arg, type('')):
-                arg = QName(arg)
-            self.args.append(arg)
-        return
-
-    def __str__(self):
-        if len(self.args) > 1:
-            return '(%s)' % ' | '.join(map(str, self.args))
-        else:
-            return str(self.args[0])
-
-    def buildValidator(self, validator, initial, final):
-        for arg in self.args:
-            arg.buildValidator(validator, initial, final)
-        return
+    return ContentModel(ContentModel.TYPE_ALT, args)
 
 
-class Rep1:
+def Rep1(arg):
     """
-    Rep1(arg)
-    matches one or more occurrences of 'arg'
+    Matches one or more occurrences of 'arg'.
     """
-    def __init__(self, arg):
-        if isinstance(arg, type('')):
-            arg = QName(arg)
-        self.arg = arg
-        return
+    assert isinstance(arg, ContentModel)
+    arg.quant = ContentModel.QUANT_PLUS
+    return arg
 
-    def __str__(self):
-        return '%s+' % str(self.arg)
 
-    def buildValidator(self, validator, initial, final):
-        state = {}
-        self.arg.buildValidator(validator, initial, state)
-        self.arg.buildValidator(validator, state, state)
-        validator.transition(ELSE, state, final)
-        return
-
-class Opt:
+def Opt(arg):
     """
-    Opt(arg)
-    matches zero or one occurrences of 'arg'
+    Matches zero or one occurrences of 'arg'
     """
+    assert isinstance(arg, ContentModel)
+    arg.quant = ContentModel.QUANT_OPT
+    return arg
 
-    def __init__(self, arg):
-        if isinstance(arg, type('')):
-            arg = QName(arg)
-        self.arg = arg
-        return
-
-    def __str__(self):
-        return '%s?' % str(self.arg)
-
-    def buildValidator(self, validator, initial, final):
-        self.arg.buildValidator(validator, initial, final)
-        return
 
-class Rep:
+def Rep(arg):
     """
-    Rep(arg)
-    matches zero or more occurrences of 'arg'
+    Matches zero or more occurrences of 'arg'
     """
-    def __init__(self, arg):
-        if isinstance(arg, type('')):
-            arg = QName(arg)
-        self.arg = arg
-        return
-
-    def __str__(self):
-        return '%s*' % str(self.arg)
-
-    def buildValidator(self, validator, initial, final):
-        self.arg.buildValidator(validator, initial, initial)
-        validator.transition(ELSE, initial, final)
-        return
+    assert isinstance(arg, ContentModel)
+    arg.quant = ContentModel.QUANT_REP
+    return arg
 
 
 # special match that matches nothing
-Empty = Atom(EMPTY)
-Empty.__str__ = lambda : '/empty/'
-Empty.__doc__ = """
-Empty is the content model for childless elements
-"""
-Empty.__nonzero__ = lambda : 0
-    
-Text = Atom(TEXT_NODE)
-Text.__str__ = lambda : '#PCDATA'
-Text.__doc__ = """\
-Text is any PCDATA content
-"""
-
-class QName(Atom):
-    """
-    QName(namespaceUri, qualifiedName)
-    matches a fully qualified name (e.g., xsl:sort)
-    """
-    def __init__(self, namespaceUri, qualifiedName):
-        self.qualifiedName = qualifiedName
-        index = qualifiedName.rfind(':')
-        if index == -1:
-            local = qualifiedName
-        else:
-            local = qualifiedName[index+1:]
-        Atom.__init__(self, (namespaceUri, local))
-        return
-
-    def __str__(self):
-        return self.qualifiedName
-
-
-ResultElements = Rep(Atom(CategoryTypes.RESULT_ELEMENT))
-ResultElements.__str__ = lambda : '/result-elements/'
-ResultElements.__doc__ = """
-ResultElements is the set of elements not declared in the XSL namespace
-"""
-
-Instructions = Rep(Atom(CategoryTypes.INSTRUCTION))
-Instructions.__str__ = lambda : '/instructions/'
-Instructions.__doc__ = """\
-Instructions is the set of elements which have a category of instruction
-"""
-
-Template = Rep(Alt(Text,
-                   QName(XSL_NAMESPACE, 'xsl:variable'),
-                   Atom(CategoryTypes.INSTRUCTION),
-                   Atom(CategoryTypes.RESULT_ELEMENT)))
-Template.__str__ = lambda : '/template/'
-Template.__doc__ = """
-Template is the set of text, instructions or result-elements
-"""
-
-TopLevelElements = Rep(Alt(QName(XSL_NAMESPACE, 'xsl:variable'),
-                           QName(XSL_NAMESPACE, 'xsl:param'),
-                           Atom(CategoryTypes.TOP_LEVEL_ELEMENT),
-                           Atom(CategoryTypes.RESULT_ELEMENT)))
-TopLevelElements.__str__ = lambda : '/top-level-elements/'
-TopLevelElements.__doc__ = """\
-TopLevelElements is the set of elements which have a category of
-top-level-element or are a result-element.
-"""
-
-class Validator:
-    def __init__(self, expr):
-        if expr is None:
-            expr = Empty
-        self._expr = expr
-        self._initial = {}
-        expr.buildValidator(self, self._initial, {})
-        return
-
-    def __str__(self):
-        return str(self._expr)
-
-    def transition(self, token, state1, state2):
-        state = state1.get(token)
-        if not state:
-            # this is the first one
-            state1[token] = state2
-        else:
-            # make new epsilon state
-            new_state = {}
-            state1[token] = new_state
-            new_state[token] = state
-            new_state[ELSE] = state2
-        return
-
-    def getValidation(self):
-        return self._initial
-    
-    def validate(self, validation, token):
-        new_state = validation.get(token, -1)
-        if new_state == -1 and validation.has_key(ELSE):
-            new_state = validation[ELSE].get(token)
-        return new_state
-        
+Empty = ContentModel(ContentModel.TYPE_NAME, EMPTY, ContentModel.QUANT_OPT,
+                     label='/empty/',
+                     doc="Empty is the content model for childless elements")
+
+Text = ContentModel(ContentModel.TYPE_NAME, TEXT_NODE,
+                    ContentModel.QUANT_REP,
+                    label="#PCDATA",
+                    doc="Text is the content model for text content")
+
+ResultElements = ContentModel(ContentModel.TYPE_NAME, RESULT_ELEMENT,
+                              ContentModel.QUANT_REP,
+                              label='/result-elements/',
+                              doc=("ResultElements is the set of elements not"
+                                   " declared in the XSL namespace"))
+
+Instructions = (QName(XSL_NAMESPACE, 'xsl:apply-templates'),
+                QName(XSL_NAMESPACE, 'xsl:call-template'),
+                QName(XSL_NAMESPACE, 'xsl:apply-imports'),
+                QName(XSL_NAMESPACE, 'xsl:for-each'),
+                QName(XSL_NAMESPACE, 'xsl:value-of'),
+                QName(XSL_NAMESPACE, 'xsl:copy-of'),
+                QName(XSL_NAMESPACE, 'xsl:number'),
+                QName(XSL_NAMESPACE, 'xsl:choose'),
+                QName(XSL_NAMESPACE, 'xsl:if'),
+                QName(XSL_NAMESPACE, 'xsl:text'),
+                QName(XSL_NAMESPACE, 'xsl:copy'),
+                QName(XSL_NAMESPACE, 'xsl:variable'),
+                QName(XSL_NAMESPACE, 'xsl:message'),
+                QName(XSL_NAMESPACE, 'xsl:fallback'),
+                QName(XSL_NAMESPACE, 'xsl:processing-instruction'),
+                QName(XSL_NAMESPACE, 'xsl:comment'),
+                QName(XSL_NAMESPACE, 'xsl:element'),
+                QName(XSL_NAMESPACE, 'xsl:attribute'))
+Instructions = ContentModel(ContentModel.TYPE_ALT, Instructions,
+                            ContentModel.QUANT_REP, label='/instructions/',
+                            doc=("Instructions is the set of elements which"
+                                 " have a category of instruction"))
+
+Template = ContentModel(ContentModel.TYPE_ALT,
+                        (Text, Instructions, ResultElements),
+                        ContentModel.QUANT_REP, label='/template/',
+                        doc=("Template is the set of text, instructions"
+                             " or result-elements"))
+
+TopLevelElements = (QName(XSL_NAMESPACE, 'xsl:include'),
+                    QName(XSL_NAMESPACE, 'xsl:strip-space'),
+                    QName(XSL_NAMESPACE, 'xsl:preserve-space'),
+                    QName(XSL_NAMESPACE, 'xsl:output'),
+                    QName(XSL_NAMESPACE, 'xsl:key'),
+                    QName(XSL_NAMESPACE, 'xsl:decimal-format'),
+                    QName(XSL_NAMESPACE, 'xsl:attribute-set'),
+                    QName(XSL_NAMESPACE, 'xsl:variable'),
+                    QName(XSL_NAMESPACE, 'xsl:param'),
+                    QName(XSL_NAMESPACE, 'xsl:template'),
+                    QName(XSL_NAMESPACE, 'xsl:namespace-alias'),
+                    ResultElements)
+TopLevelElements = ContentModel(ContentModel.TYPE_ALT, TopLevelElements,
+                                ContentModel.QUANT_REP,
+                                label='/top-level-elements/',
+                                doc=("TopLevelElements is the set of elements"
+                                     " which have a category of"
+                                     " top-level-element or are a"
+                                     " result-element."))
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ForEachElement.py.diff?r1=1.7&r2=1.8
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ForEachElement.py?rev=1.8&content-type=text/vnd.viewcvs-markup

Index: ForEachElement.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/ForEachElement.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -U2 -r1.7 -r1.8
--- ForEachElement.py	6 Apr 2005 23:05:47 -0000	1.7
+++ ForEachElement.py	27 Nov 2006 16:29:29 -0000	1.8
@@ -22,5 +22,4 @@
 class ForEachElement(XsltElement):
 
-    category = CategoryTypes.INSTRUCTION
     content = ContentInfo.Seq(
         ContentInfo.Rep(ContentInfo.QName(XSL_NAMESPACE, 'xsl:sort')),
@@ -29,6 +28,6 @@
         'select' : AttributeInfo.NodeSetExpression(required=1),
         }
-    
-    doesSetup = 1
+
+    doesSetup = True
 
     def setup(self):
@@ -49,5 +48,6 @@
             node_set = context.node.childNodes
 
-        state = context.copy()
+        children = [ child.instantiate for child in self.children ]
+        state = context.node, context.position, context.size
         pos = 1
         size = len(node_set)
@@ -55,8 +55,8 @@
             context.node, context.position, context.size = node, pos, size
             context.currentNode = node
-            for child in self.children:
-                child.instantiate(context, processor)
+            for instantiate in children:
+                instantiate(context, processor)
             pos += 1
 
-        context.set(state)
+        context.node, context.position, context.size = state
         return
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/GenericVariable.py.diff?r1=1.25&r2=1.26
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/GenericVariable.py?rev=1.26&content-type=text/vnd.viewcvs-markup

Index: GenericVariable.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/GenericVariable.py,v
retrieving revision 1.25
retrieving revision 1.26
diff -U2 -r1.25 -r1.26
--- GenericVariable.py	6 Apr 2005 23:05:47 -0000	1.25
+++ GenericVariable.py	27 Nov 2006 16:29:29 -0000	1.26
@@ -9,21 +9,16 @@
 """
 
-import warnings
-from Ft.Xml import EMPTY_NAMESPACE
-from Ft.Xml.Xslt import XsltElement, XsltException, Error, XSL_NAMESPACE
-from Ft.Xml.Xslt import ContentInfo, AttributeInfo
-from Ft.Xml.XPath import FT_EXT_NAMESPACE
-
-# for variable binding
+from Ft.Xml.Xslt import XsltElement, XSL_NAMESPACE, ContentInfo, AttributeInfo
+from Ft.Xml.Xslt import XsltException, Error
 from Ft.Xml.Xslt.StylesheetTree import XsltNode
 
+__all__ = ['GenericVariableElement', 'PushVariablesNode', 'PopVariablesNode']
+
 class GenericVariableElement(XsltElement):
 
-    category = None
     content = ContentInfo.Template
     legalAttrs = {
-        'name' : AttributeInfo.QName(required=1),
+        'name' : AttributeInfo.QName(required=True),
         'select' : AttributeInfo.Expression(),
-        'f:node-set' : AttributeInfo.YesNoAvt(default='no'),
         }
 
@@ -31,30 +26,15 @@
 
     def setup(self):
-        # Check for deprecated f:node-set
-        if (FT_EXT_NAMESPACE, 'node-set') in self.attributes:
-            warnings.warn("You are using the deprecated f:node-set attribute"
-                          " on xsl:variable or xsl:param.  Please switch to"
-                          " using exslt:node-set", DeprecationWarning, 2)
-
         # check for a bad binding
         if self._select and self.children:
             raise XsltException(Error.VAR_WITH_CONTENT_AND_SELECT, self._name)
-
-        # See the bottom of this file for these helper "nodes"
-        binding_save = self.parent.children[0]
-        if not isinstance(binding_save, PushVariablesNode):
-            # varBindings not yet saved for this level in the stylesheet tree
-            parent = self.parent
-            binding_save = PushVariablesNode(parent.root, parent.baseUri)
-            parent.insertChild(0, binding_save)
-            parent.root.primeInstructions.append(binding_save)
         return
 
     def instantiate(self, context, processor):
         # NOTE: all we want to do is change the varBindings
-        context.processorNss = self.namespaces
-        context.currentInstruction = self
 
         if self._select:
+            context.processorNss = self.namespaces
+            context.currentInstruction = self
             result = self._select.evaluate(context)
         elif self.children:
@@ -67,9 +47,4 @@
                 child.instantiate(context, processor)
             result = processor.popResult()
-            # Why is the check for childNodes necessary?  This will always
-            # be an RTF.
-            if self.attributes.get((FT_EXT_NAMESPACE, 'node-set')) == 'yes' \
-                   and hasattr(result, 'childNodes'):
-                result = [result]
         else:
             result = u""
@@ -81,16 +56,9 @@
 class PushVariablesNode(XsltNode):
 
-    def __init__(self, root, baseUri):
-        self.root = root
-        self.baseUri = baseUri
-        self.savedVariables = []
-        self.popNode = PopVariablesNode(self.savedVariables)
-        self._is_primed = False
-        return
+    isPseudoNode = True
 
-    def prime(self, processor, context):
-        if not self._is_primed:
-            self.parent.children.append(self.popNode)
-            self._is_primed = True
+    def __init__(self, root, bindingStack):
+        XsltNode.__init__(self, root)
+        self.savedVariables = bindingStack
         return
 
@@ -100,18 +68,16 @@
         return
 
-    def isPseudoNode(self):
-        return True
-
 
 class PopVariablesNode(XsltNode):
 
-    def __init__(self, savedVariables):
-        self.savedVariables = savedVariables
+    isPseudoNode = True
+
+    def __init__(self, root, bindingStack):
+        XsltNode.__init__(self, root)
+        self.savedVariables = bindingStack
         return
 
     def instantiate(self, context, processor):
-        context.varBindings = self.savedVariables.pop()
+        context.varBindings = self.savedVariables[-1]
+        del self.savedVariables[-1]
         return
-
-    def isPseudoNode(self):
-        return True
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/LiteralElement.py.diff?r1=1.12&r2=1.13
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/LiteralElement.py?rev=1.13&content-type=text/vnd.viewcvs-markup

Index: LiteralElement.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/LiteralElement.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -U2 -r1.12 -r1.13
--- LiteralElement.py	6 Apr 2005 23:05:47 -0000	1.12
+++ LiteralElement.py	27 Nov 2006 16:29:29 -0000	1.13
@@ -12,4 +12,6 @@
 from Ft.Xml.Xslt import XsltElement, XsltRuntimeException, Error
 
+__all__ = ['LiteralElement']
+
 class LiteralElement(XsltElement):
 
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/Processor.py.diff?r1=1.80&r2=1.81
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/Processor.py?rev=1.81&content-type=text/vnd.viewcvs-markup

Index: Processor.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/Processor.py,v
retrieving revision 1.80
retrieving revision 1.81
diff -U2 -r1.80 -r1.81
--- Processor.py	24 Nov 2006 22:47:36 -0000	1.80
+++ Processor.py	27 Nov 2006 16:29:29 -0000	1.81
@@ -175,4 +175,6 @@
                 if hasattr(mod,'ExtElements'):
                     self.extElements.update(mod.ExtElements)
+                    reader = self.getStylesheetReader()
+                    reader.addExtensionElementMapping(mod.ExtElements)
         return
 
@@ -206,4 +208,7 @@
         """
         self.extElements[(namespace, localName)] = klass
+        reader = self.getStylesheetReader()
+        mapping = { (namespace, localName) : klass }
+        reader.addExtensionElementMapping(mapping)
         return
 
@@ -245,28 +250,4 @@
         return self._styReader
 
-    def __add_stylesheet(self, stylesheet):
-        """
-        INTERNAL USE ONLY
-        Helper function for adding a stylesheet to the processor.  If a
-        stylesheet has already been appended, then this method is equivalent
-        to having, in an outer "shell" stylesheet, an xsl:import for the most
-        recently appended stylesheet followed by an xsl:import for the given
-        stylesheet.
-        """
-        if self.stylesheet:
-            for child in self.stylesheet.children:
-                child.importIndex += 1000
-
-            for child in stylesheet.children:
-                self.stylesheet.appendChild(child)
-
-            self.stylesheet.reset()
-            self.stylesheet.setup()
-        else:
-            self.stylesheet = stylesheet
-
-        self.outputParams = self.stylesheet.outputParams
-        return
-
     def appendStylesheet(self, iSrc):
         """
@@ -280,6 +261,5 @@
         """
         reader = self.getStylesheetReader()
-        stylesheet = reader.fromSrc(iSrc, self.extElements)
-        self.__add_stylesheet(stylesheet)
+        self.stylesheet = reader.fromSrc(iSrc)
         return
 
@@ -308,13 +288,6 @@
         stylesheet is large and complex, like DocBook XSL.
         """
-        baseUri = refUri
-        if not baseUri:
-            # StylesheetTree nodes only ever have baseUri
-            # (not baseURI, documentURI, or refUri)
-            if hasattr(instance.root, 'baseUri'):
-                baseUri = instance.root.baseUri
         reader = self.getStylesheetReader()
-        stylesheet = reader.fromInstant(instance, baseUri=baseUri, is_import=True)
-        self.__add_stylesheet(stylesheet)
+        self.stylesheet = reader.fromInstant(instance, refUri)
         return
 
@@ -339,6 +312,5 @@
         document = node.ownerDocument or node
         reader = self.getStylesheetReader()
-        stylesheet = reader.fromDocument(document, refUri, factory)
-        self.__add_stylesheet(stylesheet)
+        self.stylesheet = reader.fromDocument(document, refUri, factory)
         return
 
@@ -647,5 +619,4 @@
 
         if hrefs:
-            self.getStylesheetReader()._import_index += 1
             for href in hrefs:
                 # Resolve the PI with the InputSource for the document
@@ -686,4 +657,5 @@
         if not self.stylesheet:
             raise XsltException(Error.NO_STYLESHEET)
+        self.outputParams = self.stylesheet.outputParams
 
         # Use an internal stream to gather the output only if the caller
@@ -938,5 +910,4 @@
         """
         return self.writers[-1]
-
     writer = property(writer)
 
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/Stylesheet.py.diff?r1=1.53&r2=1.54
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/Stylesheet.py?rev=1.54&content-type=text/vnd.viewcvs-markup

Index: Stylesheet.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/Stylesheet.py,v
retrieving revision 1.53
retrieving revision 1.54
diff -U2 -r1.53 -r1.54
--- Stylesheet.py	30 Jul 2006 16:45:47 -0000	1.53
+++ Stylesheet.py	27 Nov 2006 16:29:29 -0000	1.54
@@ -69,8 +69,8 @@
 
     # We don't want ourselves included in these lists since we do the walking
-    doesSetup = doesPrime = doesIdle = 0
+    doesSetup = doesPrime = doesIdle = False
 
-    def __init__(self, root, namespaceUri, localName, baseUri):
-        XsltElement.__init__(self, root, namespaceUri, localName, baseUri)
+    def __init__(self, *args, **kwds):
+        XsltElement.__init__(self, *args, **kwds)
         self.reset1()
         return
@@ -357,19 +357,30 @@
             if not isinstance(split_name, tuple):
                 try:
-                    split_name = self.expandQName(split_name)
+                    split_name = context.expandQName(split_name)
                 except KeyError:
                     continue
             overridden_params[split_name] = value
 
+        processed, deferred = {}, []
         for vnode in self._topVariables[1]:
-            self._computeGlobalVar(vnode._name, context, [], [],
-                                   overridden_params, processor)
+            name = vnode._name
+            if vnode.expandedName[1][0] == 'p':
+                if name in overridden_params:
+                    context.varBindings[name] = overridden_params[name]
+                else:
+                    self._computeGlobalVar(name, context, processed, deferred,
+                                           processor)
+                    #Set up so that later stylesheets will get overridden by
+                    #parameter values set in higher-priority stylesheets
+                    overridden_params[name] = context.varBindings[name]
+            else:
+                self._computeGlobalVar(name, context, processed, deferred,
+                                       processor)
             self.globalVars.update(context.varBindings)
-
         return
 
 
     def _computeGlobalVar(self, vname, context, processed, deferred,
-                          overriddenParams, processor):
+                          processor):
         vnode = self._topVariables[0][vname]
 
@@ -378,61 +389,27 @@
         if vnode in processed:
             return
-        # Is it an <xsl:param>?
-        # expandedName is a tuple (namespace-uri, local-name)
-        if vnode.expandedName[1][0] == 'p':
-            if vname in overriddenParams:
-                context.varBindings[vname] = overriddenParams[vname]
+        while True:
+            depth = len(processor.writers)
+            try:
+                vnode.instantiate(context, processor)
+            except XPath.RuntimeException, e:
+                if e.errorCode == XPath.RuntimeException.UNDEFINED_VARIABLE:
+                    #Remove any aborted and possibly unbalanced
+                    #outut handlers on the stack
+                    depth -= len(processor.writers)
+                    if depth:
+                        assert depth < 0
+                        del processor.writers[depth:]
+                    #Defer the current and try evaluating the
+                    #one that turned up undefined
+                    deferred.append(vnode)
+                    self._computeGlobalVar(e.params['key'], context, processed,
+                                           deferred, processor)
+                    deferred.remove(vnode)
+                else:
+                    raise
             else:
-                finished = 0
-                while not finished:
-                    orig_depth = len(processor.writers)
-                    try:
-                        vnode.instantiate(context, processor)
-                        finished = 1
-                    except XPath.RuntimeException, e:
-                        if e.errorCode == XPath.RuntimeException.UNDEFINED_VARIABLE:
-                            #Remove any aborted and possibly unbalanced
-                            #outut handlers on the stack
-                            depth = len(processor.writers)
-                            for i in xrange(depth - orig_depth):
-                                processor.writers.pop()
-                            #Defer the current and try evaluating the
-                            #one that turned up undefined
-                            deferred.append(vnode)
-                            self._computeGlobalVar((e.params[0], e.params[1]),
-                                                   context, processed, deferred,
-                                                   overriddenParams,
-                                                   processor)
-                            deferred.remove(vnode)
-                        else:
-                            raise
-                #Set up so that later stylesheets will get overridden by
-                #parameter values set in higher-priority stylesheets
-                overriddenParams[vname] = context.varBindings[vname]
-        else:
-            finished = 0
-            while not finished:
-                orig_depth = len(processor.writers)
-                try:
-                    vnode.instantiate(context, processor)
-                    finished = 1
-                except XPath.RuntimeException, e:
-                    if e.errorCode == XPath.RuntimeException.UNDEFINED_VARIABLE:
-                        #Remove any aborted and possibly unbalanced
-                        #output handlers on the stack
-                        depth = len(processor.writers)
-                        for i in xrange(depth - orig_depth):
-                            processor.writers.pop()
-                        #Defer the current and try evaluating the
-                        #one that turned up undefined
-                        deferred.append(vnode)
-                        self._computeGlobalVar((e.params[0], e.params[1]),
-                                               context, processed, deferred,
-                                               overriddenParams,
-                                               processor)
-                        deferred.remove(vnode)
-                    else:
-                        raise
-        processed.append(vnode)
+                break
+        processed[vnode] = True
         return
 
@@ -563,5 +540,5 @@
         # with a higher import precedence than what was specified.
         if maxImport is not None:
-            patterns = filter(lambda x, m=maxImport: x[0][0] < m, patterns)
+            patterns = [ x for x in patterns if x[0][0] < maxImport ]
 
         # Since the patterns may come from different tables, resort them
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/StylesheetHandler.py.diff?r1=1.61&r2=1.62
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/StylesheetHandler.py?rev=1.62&content-type=text/vnd.viewcvs-markup

Index: StylesheetHandler.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/StylesheetHandler.py,v
retrieving revision 1.61
retrieving revision 1.62
diff -U2 -r1.61 -r1.62
--- StylesheetHandler.py	14 Sep 2005 21:38:45 -0000	1.61
+++ StylesheetHandler.py	27 Nov 2006 16:29:29 -0000	1.62
@@ -4,5 +4,5 @@
 Stylesheet tree generator
 
-Copyright 2004 Fourthought, Inc. (USA).
+Copyright 2006 Fourthought, Inc. (USA).
 Detailed license and copyright information: http://4suite.org/COPYRIGHT
 Project home, documentation, distributions: http://4suite.org/
@@ -10,18 +10,20 @@
 
 from Ft.Lib import Truncate, UriException
-from Ft.Xml import XML_NAMESPACE, EMPTY_NAMESPACE, Domlette
+from Ft.Xml import XML_NAMESPACE
 from Ft.Xml.Lib.XmlString import IsXmlSpace
-from Ft.Xml.Xslt import XSL_NAMESPACE, MessageSource
+from Ft.Xml.Xslt import XSL_NAMESPACE, ContentInfo, AttributeInfo
 from Ft.Xml.Xslt import XsltException, XsltParserException, Error
-from Ft.Xml.Xslt import CategoryTypes, BuiltInExtElements
-from Ft.Xml.Xslt import Exslt
+from Ft.Xml.Xslt import BuiltInExtElements, Exslt, StylesheetTree
+from Ft.Xml.Xslt import MessageSource
 
-from LiteralElement import LiteralElement
-from UndefinedElements import UndefinedXsltElement, UndefinedExtensionElement
+# These are `__all__` safe
+from GenericVariable import *
+from LiteralElement import *
+from UndefinedElements import *
 
-import StylesheetTree, ContentInfo, AttributeInfo
+__all__ = ['StylesheetHandler']
 
 # Table for load-on-demand of the XSLT elements
-_ELEMENT_MAPPING = {
+ELEMENT_MAPPING = {
     'apply-templates' : 'ApplyTemplatesElement.ApplyTemplatesElement',
     'apply-imports' : 'ApplyImportsElement.ApplyImportsElement',
@@ -62,5 +64,5 @@
 
 # The XSL attributes allowed on literal elements
-_RESULT_ELEMENT_XSL_ATTRS = {
+RESULT_ELEMENT_XSL_ATTRS = {
     'exclude-result-prefixes' : AttributeInfo.Prefixes(),
     'extension-element-prefixes' : AttributeInfo.Prefixes(),
@@ -68,8 +70,11 @@
     'version' : AttributeInfo.Number(),
     }
-_RESULT_ELEMENT_ATTR_INFO = AttributeInfo.AnyAvt()
+RESULT_ELEMENT_ATTR_INFO = AttributeInfo.AnyAvt()
+
+# Cached values for stylesheet tree creation
+_XSLT_ELEMENT_CACHE = {}
+_XSLT_ROOT_VALIDATION = StylesheetTree.XsltRoot.content.compile()
+_LITERAL_ELEMENT_VALIDATION = LiteralElement.content.compile()
 
-_ELEMENT_CLASSES = {}
-_LEGAL_ATTRS = {}
 
 class ParseState:
@@ -92,5 +97,5 @@
       outputNamespaces - set of in-scope namespaces for literal result elements
     """
-    
+
     def __init__(self, node, validation, localVariables, forwardsCompatible,
                  currentNamespaces, extensionNamespaces, outputNamespaces):
@@ -111,40 +116,32 @@
     """
 
-    def __init__(self, importIndex=0, globalVars=None, extElements=None,
-                 visitedStyUris=None, altBaseUris=None, ownerDocument=None):
-        self._import_index = importIndex
-        if globalVars is None:
-            # We need to make sure that the same dictionary is used
-            # through the entire processing (even if empty)
-            self._global_vars = {}
-        else:
-            self._global_vars = globalVars
-        if extElements is None:
-            self._extElements = d = {}
-            d.update(Exslt.ExtElements)
-            d.update(BuiltInExtElements.ExtElements)
-        else:
-            self._extElements = extElements
+    # runtime instance variables
+    _input_source = None
+    _locator = None
+    _stylesheet = None
 
-        self._visited_stylesheet_uris = visitedStyUris or {}
+    def __init__(self, altBaseUris=None):
         self._alt_base_uris = altBaseUris or []
-        self._ownerDoc = ownerDocument
+        self._ownerDoc = None
+        self._import_index = 0
+        self._global_vars = {}
+        self._visited_stylesheet_uris = {}
+        self._documentStateStack = []
+        self._elementStateStack = []
+        self._extElements = {}
+        self._extElements.update(Exslt.ExtElements)
+        self._extElements.update(BuiltInExtElements.ExtElements)
+        self._extensionElementCache = {}
         return
 
     def reset(self):
-        self._global_vars = {}
+        self._ownerDoc = None
         self._import_index = 0
+        self._global_vars = {}
         self._visited_stylesheet_uris = {}
-        self._ownerDoc = None
+        self._documentStateStack = []
+        self._elementStateStack = []
         return
 
-    def clone(self):
-        return self.__class__(self._import_index, self._global_vars,
-                              self._extElements, self._visited_stylesheet_uris,
-                              self._alt_base_uris, self._ownerDoc)
-
-    def getResult(self):
-        return self._ownerDoc
-
     def addExtensionElementMapping(self, elementMapping):
         """
@@ -158,6 +155,8 @@
         You have been warned.
         """
-        self._extElements = self._extElements.copy()
         self._extElements.update(elementMapping)
+        for name in elementMapping:
+            if name in self._extensionElementCache:
+                del self._extensionElementCache[name]
         return
 
@@ -165,10 +164,17 @@
 
     def setDocumentLocator(self, locator):
+        """
+        Callback interface for SAX.
+        """
+        # Save the current document state for nested parsing (inclusions)
+        document_state = (self._locator, self._stylesheet)
+        self._documentStateStack.append(document_state)
         self._locator = locator
+        self._stylesheet = None
         return
 
     def startDocument(self):
         """
-        ownerDoc is supplied when processing an XSLT import or include.
+        Callback interface for SAX.
         """
         # Our root is always a document
@@ -182,16 +188,14 @@
             self._ownerDoc = root
 
-        # the stylesheet element instance
-        self._stylesheet = None
-
-        self._state_stack = [
+        self._elementStateStack.append(
             ParseState(node=root,
-                       validation=root.validator.getValidation(),
+                       validation=_XSLT_ROOT_VALIDATION,
                        localVariables={},
                        forwardsCompatible=False,
                        currentNamespaces={'xml' : XML_NAMESPACE, None : None},
                        extensionNamespaces={},
-                       outputNamespaces={})
-            ]
+                       outputNamespaces={},
+                       )
+            )
 
         # for recursive include checks for xsl:include/xsl:import
@@ -203,14 +207,76 @@
 
     def endDocument(self):
-        self._import_index += 1
-        self._locator = None
+        """
+        Callback interface for SAX.
+        """
+        stack = self._elementStateStack
+        state = stack[-1]
+        del stack[-1]
+        root = state.node
+
+        # ----------------------------------------------------------
+        # remove URI from recursive inclusion checking
+        del self._visited_stylesheet_uris[root.baseUri]
+
+        # ----------------------------------------------------------
+        # finalize the children for the document
+        #root.children = tuple(state.nodes)
+
+        # ----------------------------------------------------------
+        # finalize the stylesheet AST
+        if stack:
+            # An xsl:import or xsl:include
+            # Merge the top-level elements into the "parent" stylesheet
+            # IMPLEMENTATION NOTE: stack[-1] is the import/include element,
+            # stack[-2] is the "parent" stylesheet
+            stack[-2].node._merge(self._stylesheet)
+            #parent_node = stack[-2].node
+            #for child in self._stylesheet.children:
+            #    child.parent = parent_node
+        else:
+            # A top-most stylesheet
+            stylesheet = self._ownerDoc.stylesheet
+            if stylesheet is not self._stylesheet:
+                # An additional stylesheet (e.g., an <?xml-stylesheet ...?>);
+                # treat it as an xsl:import into the "master" stylesheet.
+                stylesheet.reset()
+
+                # Always update the precedence from the included stylesheet
+                # because it may have contained imports thus increasing its
+                # import precedence.
+                self._import_index += 1
+                stylesheet.importIndex = self._import_index
+
+                # Merge the top-level elements into the "master" stylesheet
+                stylesheet._merge(self._stylesheet)
+                #stylesheet.children += self._stylesheet.children
+                #for child in self._stylesheet.children:
+                #    child.parent = stylesheet
+            else:
+                # Prepare for a possible subsequent parse.
+                self._import_index += 1
+
+            # Prepare the "master" stylesheet
+            stylesheet.setup()
+
+        document_state = self._documentStateStack[-1]
+        del self._documentStateStack[-1]
+        self._locator, self._stylesheet = document_state
         return
 
     def startPrefixMapping(self, prefix, uri):
+        """
+        Callback interface for SAX.
+        """
         self._new_namespaces[prefix] = uri
         return
 
     def startElementNS(self, expandedName, qualifiedName, attribs):
-        state = ParseState(**self._state_stack[-1].__dict__)
+        """
+        Callback interface for SAX.
+        """
+        parent_state = self._elementStateStack[-1]
+        state = ParseState(**parent_state.__dict__)
+        self._elementStateStack.append(state)
 
         # ----------------------------------------------------------
@@ -221,5 +287,5 @@
 
             d = state.outputNamespaces = state.outputNamespaces.copy()
-            for prefix, uri in self._new_namespaces.items():
+            for prefix, uri in self._new_namespaces.iteritems():
                 if uri not in (XML_NAMESPACE, XSL_NAMESPACE):
                     d[prefix] = uri
@@ -227,17 +293,17 @@
             # reset for next element
             self._new_namespaces = {}
-        
+
         # ----------------------------------------------------------
         # get the class defining this element
         namespace, local = expandedName
         xsl_class = ext_class = None
-        category = CategoryTypes.RESULT_ELEMENT
         if namespace == XSL_NAMESPACE:
             try:
-                xsl_class = _ELEMENT_CLASSES[local]
+                xsl_class, validation, validation_token, legal_attrs = \
+                    _XSLT_ELEMENT_CACHE[local]
             except KeyError:
                 # We need to try to import (and cache) it
                 try:
-                    module = _ELEMENT_MAPPING[local]
+                    module = ELEMENT_MAPPING[local]
                 except KeyError:
                     if not state.forwardsCompatible:
@@ -245,4 +311,5 @@
                                                   self._locator, local)
                     xsl_class = UndefinedXsltElement
+                    validation_token = ContentInfo.RESULT_ELEMENT
                 else:
                     parts = module.split('.')
@@ -253,37 +320,37 @@
                     except KeyError:
                         raise ImportError('.'.join(parts))
-                _ELEMENT_CLASSES[local] = xsl_class
-                _LEGAL_ATTRS[xsl_class] = xsl_class.legalAttrs.items()
-                xsl_class.validator = ContentInfo.Validator(xsl_class.content)
-            category = xsl_class.category
+                    validation_token = expandedName
+                validation = xsl_class.content.compile()
+                legal_attrs = xsl_class.legalAttrs.items()
+                _XSLT_ELEMENT_CACHE[local] = (
+                    xsl_class, validation, validation_token, legal_attrs)
         elif namespace in state.extensionNamespaces:
             try:
-                ext_class = self._extElements[(namespace, local)]
+                ext_class, validation, legal_attrs = \
+                    self._extensionElementCache[expandedName]
             except KeyError:
-                ext_class = UndefinedExtensionElement
-            else:
-                if ext_class not in _LEGAL_ATTRS:
-                    ext_class.validator = \
-                        ContentInfo.Validator(ext_class.content)
-                    legal_attrs = ext_class.legalAttrs
-                    if legal_attrs is not None:
-                        _LEGAL_ATTRS[ext_class] = legal_attrs.items()
+                try:
+                    ext_class = self._extElements[expandedName]
+                except KeyError:
+                    ext_class = UndefinedExtensionElement
+                validation = ext_class.content.compile()
+                legal_attrs = ext_class.legalAttrs
+                if legal_attrs is not None:
+                    legal_attrs = ext_class.legalAttrs.items()
+                self._extensionElementCache[expandedName] = (
+                    ext_class, validation, legal_attrs)
+            validation_token = ContentInfo.RESULT_ELEMENT
+        else:
+            validation = _LITERAL_ELEMENT_VALIDATION
+            validation_token = ContentInfo.RESULT_ELEMENT
+        state.validation = validation
 
         # ----------------------------------------------------------
         # verify that this element can be declared here
-        validation_else = ContentInfo.ELSE
-        if category is not None:
-            next = state.validation.get(category)
-            if next is None and validation_else in state.validation:
-                next = state.validation[validation_else].get(category)
-        else:
-            next = None
-        if next is None:
-            next = state.validation.get(expandedName)
-            if next is None and validation_else in state.validation:
-                next = state.validation[ContentInfo.ELSE].get(expandedName)
-        if next is None:
+        try:
+            next = parent_state.validation[validation_token]
+        except KeyError:
             #self._debug_validation(expandedName)
-            parent = state.node
+            parent = parent_state.node
             if parent is self._stylesheet:
                 if (XSL_NAMESPACE, 'import') == expandedName:
@@ -297,5 +364,5 @@
             # element as an exception will occur when/if this element
             # is actually instantiated
-            if not isinstance(parent, UndefinedExtensionElement):
+            if parent.__class__ is not UndefinedExtensionElement:
                 raise XsltParserException(Error.ILLEGAL_ELEMENT_CHILD,
                                           self._locator, qualifiedName,
@@ -303,22 +370,23 @@
         else:
             # save this state for next go round
-            self._state_stack[-1].validation = next
+            parent_state.validation = next
 
         # ----------------------------------------------------------
         # create the instance defining this element
         klass = (xsl_class or ext_class or LiteralElement)
-        instance = klass(self._ownerDoc, namespace, local,
-                         self._locator.getSystemId())
+        state.node = instance = klass(self._ownerDoc, namespace, local,
+                                      qualifiedName)
+        instance.baseUri = self._locator.getSystemId()
         instance.lineNumber = self._locator.getLineNumber()
         instance.columnNumber = self._locator.getColumnNumber()
         instance.importIndex = self._import_index
         instance.namespaces = state.currentNamespaces
-        instance.nodeName = qualifiedName
-        
+
         # -- XSLT element --------------------------------------
         if xsl_class:
             # Handle attributes in the null-namespace
+            standand_attributes = local in ('stylesheet', 'transform')
             inst_dict = instance.__dict__
-            for attr_name, attr_info in _LEGAL_ATTRS[xsl_class]:
+            for attr_name, attr_info in legal_attrs:
                 attr_expanded = (None, attr_name)
                 if attr_expanded in attribs:
@@ -334,7 +402,7 @@
                     value = attr_info.prepare(instance, value)
                 except XsltException, e:
-                    raise self._mutate_exception(e)
+                    raise self._mutate_exception(e, qualifiedName)
 
-                if local in ('stylesheet', 'transform'):
+                if standand_attributes:
                     self._stylesheet = instance
                     self._handle_standard_attr(state, instance, attr_name,
@@ -356,5 +424,6 @@
                                 self._locator, attr_name, qualifiedName)
                     else:
-                        instance.attributes[expanded] = attribs[expanded]
+                        instance.setAttribute(attr_ns, attr_name,
+                                              attribs[expanded])
 
             # XSLT Spec 2.6 - Combining Stylesheets
@@ -364,9 +433,9 @@
         # -- extension element ---------------------------------
         elif ext_class:
-            validate_attributes = (ext_class in _LEGAL_ATTRS)
+            validate_attributes = (legal_attrs is not None)
             if validate_attributes:
                 # Handle attributes in the null-namespace
                 inst_dict = instance.__dict__
-                for attr_name, attr_info in _LEGAL_ATTRS[ext_class]:
+                for attr_name, attr_info in legal_attrs:
                     attr_expanded = (None, attr_name)
                     if attr_expanded in attribs:
@@ -382,5 +451,5 @@
                         value = attr_info.prepare(instance, value)
                     except XsltException, e:
-                        raise self._mutate_exception(e)
+                        raise self._mutate_exception(e, qualifiedName)
                     if '-' in attr_name:
                         attr_name = attr_name.replace('-', '_')
@@ -402,5 +471,5 @@
                                                          attr_name, value)
                     else:
-                        instance.attributes[expanded] = value
+                        instance.setAttribute(attr_ns, attr_name, value)
 
         # -- literal result element ----------------------------
@@ -415,7 +484,6 @@
                                                      attr_local, value)
                 else:
-                    instance.attributes[expanded] = value
                     # prepare attributes for literal output
-                    value = _RESULT_ELEMENT_ATTR_INFO.prepare(instance, value)
+                    value = RESULT_ELEMENT_ATTR_INFO.prepare(instance, value)
                     attr_qname = attribs.getQNameByName(expanded)
                     output_attrs.append((attr_qname, attr_ns, value))
@@ -427,16 +495,9 @@
 
             # Check for top-level result-element in null namespace
-            parent = state.node
-            if parent is self._stylesheet and \
+            if parent_state.node is self._stylesheet and \
                    not namespace and not state.forwardsCompatible:
                 raise XsltParserException(Error.ILLEGAL_ELEMENT_CHILD,
                                           self._locator, qualifiedName,
-                                          parent.nodeName)
-
-        # ----------------------------------------------------------
-        # update depth information
-        state.node = instance
-        state.validation = instance.validator.getValidation()
-        self._state_stack.append(state)
+                                          parent_state.node.nodeName)
 
         if instance.doesPrime:
@@ -447,8 +508,47 @@
 
     def endElementNS(self, expandedName, qualifiedName):
-        state = self._state_stack.pop()
+        """
+        Callback interface for SAX.
+        """
+        stack = self._elementStateStack
+        state = stack[-1]
+        del stack[-1]
+        parent_state = stack[-1]
         element = state.node
-        if len(self._state_stack) == 1 and isinstance(element, LiteralElement):
+
+        # ----------------------------------------------------------
+        # verify that this element has all required content
+        try:
+            state.validation[ContentInfo.END_ELEMENT]
+        except KeyError:
+            if expandedName == (XSL_NAMESPACE, u'choose'):
+                raise XsltParserException(Error.CHOOSE_REQUIRES_WHEN,
+                                          self._locator)
+            raise
+
+        # ----------------------------------------------------------
+        # setup variable context
+        if state.localVariables is not parent_state.localVariables:
+            # add context save/restore nodes
+            binding_stack = []
+            node = PushVariablesNode(self._ownerDoc, binding_stack)
+            element.insertChild(0, node)
+            node = PopVariablesNode(self._ownerDoc, binding_stack)
+            element.appendChild(node)
+
+        # ----------------------------------------------------------
+        # finalize the children for this element
+        #element.children = tuple(state.nodes)
+        #for child in element.children:
+        #    if child.doesSetup:
+        #s        child.setup()
+        del state
+
+        # ----------------------------------------------------------
+        # update parent state
+        parent_node = parent_state.node
+        if self._stylesheet is None and parent_node is element.root:
             # a literal result element as stylesheet
+            assert isinstance(element, LiteralElement), element
             try:
                 version = element._version
@@ -457,4 +557,7 @@
                                           self._locator)
 
+            # Reset the root's validation as it has already seen an element.
+            parent_state.validation = _XSLT_ROOT_VALIDATION
+
             # FIXME: use the prefix from the document for the XSL namespace
             stylesheet = (XSL_NAMESPACE, u'stylesheet')
@@ -467,70 +570,73 @@
 
             # make this element the template's content
-            self._state_stack[-1].node.appendChild(element)
+            # Note, this MUST index the stack as the stack has changed
+            # due to the startElementNS() calls.
+            stack[-1].node.appendChild(element)
 
             self.endElementNS(template, u'xsl:template')
             self.endElementNS(stylesheet, u'xsl:stylesheet')
-        else:
-            self._state_stack[-1].node.appendChild(element)
+            return
 
-            if expandedName in ((XSL_NAMESPACE, u'variable'),
-                                (XSL_NAMESPACE, u'param')):
-                name = element._name
-                # one for the root and one for the stylesheet or
-                # a literal result element as stylesheet
-                if len(self._state_stack) > 2 or \
-                   isinstance(self._state_stack[-1].node, LiteralElement):
-                    # local variables
-                    # it is safe to ignore import precedence here
-                    local_vars = self._state_stack[-1].localVariables
-                    if name in local_vars:
-                        raise XsltParserException(Error.ILLEGAL_SHADOWING,
-                                                  self._locator, name)
-                    # Copy on use
-                    if local_vars is self._state_stack[-2].localVariables:
-                        local_vars = local_vars.copy()
-                        self._state_stack[-1].localVariables = local_vars
-                    local_vars[name] = True
-                else:
-                    # global variables
-                    existing = self._global_vars.get(name, -1)
+        parent_node.appendChild(element)
+
+        if isinstance(element, GenericVariableElement):
+            name = element._name
+            if parent_node is self._stylesheet:
+                # global variables
+                if name in self._global_vars:
+                    existing = self._global_vars[name]
                     if self._import_index > existing:
                         self._global_vars[name] = self._import_index
                     elif self._import_index == existing:
-                        raise XsltParserException(Error.DUPLICATE_TOP_LEVEL_VAR,
-                                                  self._locator, name)
+                        raise XsltParserException(
+                            Error.DUPLICATE_TOP_LEVEL_VAR,
+                            self._locator, name)
+                else:
+                    self._global_vars[name] = self._import_index
+            else:
+                # local variables
+                # it is safe to ignore import precedence here
+                local_vars = parent_state.localVariables
+                if name in local_vars:
+                    raise XsltParserException(Error.ILLEGAL_SHADOWING,
+                                              self._locator, name)
+                # Copy on use
+                if local_vars is stack[-2].localVariables:
+                    local_vars = local_vars.copy()
+                    parent_state.localVariables = local_vars
+                local_vars[name] = True
         return
 
     def characters(self, data):
-        state = self._state_stack[-1]
+        """
+        Callback interface for SAX.
+        """
+        parent_state = self._elementStateStack[-1]
         # verify that the current element can have text children
-        validation = state.validation
-        token = ContentInfo.TEXT_NODE
-        next = validation.get(token)
-        if next is None and ContentInfo.ELSE in validation:
-            next = validation[ContentInfo.ELSE].get(token)
-        if next is None:
+        try:
+            next = parent_state.validation[ContentInfo.TEXT_NODE]
+        except KeyError:
             # If the parent can have element children, but not text nodes,
             # ignore pure whitespace nodes. This clarification is from
             # XSLT 2.0 [3.4] Whitespace Stripping.
             # e.g. xsl:stylesheet, xsl:apply-templates, xsl:choose
-            if not (ContentInfo.EMPTY not in validation and IsXmlSpace(data)):
+            #self._debug_validation(ContentInfo.TEXT_NODE)
+            if (ContentInfo.EMPTY in parent_state.validation or
+                not IsXmlSpace(data)):
                 raise XsltParserException(Error.ILLEGAL_TEXT_CHILD_PARSE,
                                           self._locator,
                                           repr(Truncate(data, 10)),
-                                          state.node.nodeName)
-            #self._debug_validation(expandedName)
+                                          parent_state.node.nodeName)
+            #self._debug_validation(ContentInfo.TEXT_NODE)
         else:
             # update validation
-            state.validation = next
+            parent_state.validation = next
 
-            node = StylesheetTree.XsltText(self._ownerDoc,
-                                           self._locator.getSystemId(),
-                                           data)
-            state.node.appendChild(node)
+            node = StylesheetTree.XsltText(self._ownerDoc, data)
+            parent_state.node.appendChild(node)
         return
 
     # -- utility functions ---------------------------------------------
-    
+
     def _combine_stylesheet(self, href, is_import):
         hint = is_import and 'STYLESHEET IMPORT' or 'STYLESHEET INCLUDE'
@@ -558,33 +664,10 @@
             raise XsltParserException(Error.CIRCULAR_INCLUDE,
                                       self._locator, new_source.uri)
-        else:
-            self._visited_stylesheet_uris[new_source.uri] = True
-
-        # Create a new reader to handle the inclusion
-        include = self.clone().fromSrc(new_source)
+        self.fromSrc(new_source)
 
-        # Make sure we detect circular imports/includes, but not other duplicates
-        del self._visited_stylesheet_uris[new_source.uri]
-
-        # The stylesheet containing the import will always be one higher
-        # than the imported stylesheet.
-        #
-        # For example:
-        #  stylesheet A imports stylesheets B and C in that order,
-        #  stylesheet B imports stylesheet D,
-        #  stylesheet C imports stylesheet E.
-        # The resulting import precedences are:
-        #  A=4, C=3, E=2, B=1, D=0
-
-        # Always update the precedence from the included stylesheet
-        # because it may have contained imports thus increasing its
-        # import precedence.
-        import_index = include.importIndex + is_import
-        self._stylesheet.importIndex = self._import_index = import_index
-
-        # merge the top-level elements
-        self._stylesheet.children.extend(include.children)
-        for child in include.children:
-            child.parent = self._stylesheet
+        self._import_index += is_import
+        # Always update the precedence as the included stylesheet may have
+        # contained imports thus increasing the import precedence.
+        self._stylesheet.importIndex = self._import_index
         return
 
@@ -619,5 +702,5 @@
                                               self._locator,
                                               prefix or '#default')
-                # remove all matching namespace URIs 
+                # remove all matching namespace URIs
                 for output_prefix, output_uri in out.items():
                     if output_uri == uri:
@@ -636,5 +719,5 @@
                                     attributeName, value):
         try:
-            attr_info = _RESULT_ELEMENT_XSL_ATTRS[attributeName]
+            attr_info = RESULT_ELEMENT_XSL_ATTRS[attributeName]
         except KeyError:
             raise XsltParserException(Error.ILLEGAL_XSL_NAMESPACE_ATTR,
@@ -645,32 +728,27 @@
         return
 
-    def _mutate_exception(self, exception):
-        msg = MessageSource.POSITION_INFO % (self._locator.getSystemId(),
-                                             self._locator.getLineNumber(),
-                                             self._locator.getColumnNumber(),
-                                             exception.message)
-        exception.message = msg
+    def _mutate_exception(self, exception, elementName):
+        assert isinstance(exception, XsltException)
+        exception.message = MessageSource.EXPRESSION_POSITION_INFO % (
+            self._locator.getSystemId(), self._locator.getLineNumber(),
+            self._locator.getColumnNumber(), elementName, exception.message)
         return exception
 
-
     # -- debugging routines --------------------------------------------
-    
-    def _debug_validation(self, token=None, next=None):
+
+    def _debug_validation(self, token=None):
         from pprint import pprint
-        state = self._state_stack[-1]
+        state = self._elementStateStack[-1]
         parent = state.node
         print '='*60
         print 'parent =',parent
         print 'parent class =',parent.__class__
-        print 'content expression =', parent.validator
+        print 'parent content =', parent.content
         print 'initial validation'
-        pprint(parent.validator.getValidation())
+        pprint(parent.content.compile())
         print 'current validation'
         pprint(state.validation)
         if token:
             print 'token', token
-        if next:
-            print 'next validation'
-            pprint(next)
         print '='*60
         return
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/StylesheetReader.py.diff?r1=1.56&r2=1.57
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/StylesheetReader.py?rev=1.57&content-type=text/vnd.viewcvs-markup

Index: StylesheetReader.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/StylesheetReader.py,v
retrieving revision 1.56
retrieving revision 1.57
diff -U2 -r1.56 -r1.57
--- StylesheetReader.py	29 Oct 2005 05:34:25 -0000	1.56
+++ StylesheetReader.py	27 Nov 2006 16:29:29 -0000	1.57
@@ -9,4 +9,6 @@
 """
 
+__all__ = ['StylesheetReader']
+
 import cStringIO
 from xml.dom import Node
@@ -27,5 +29,4 @@
     return
 
-
 # Whitespace stripping rules for a stylesheet:
 #   preserve all whitespace within xsl:text elements;
@@ -44,23 +45,4 @@
     avoids creating a Domlette document for each document it reads.
     """
-    def __init__(self, altBaseUris=None, ownerDocument=None,
-                 importIndex=0, globalVars=None, extElements=None,
-                 visitedStyUris=None):
-        StylesheetHandler.__init__(self, importIndex, globalVars, extElements,
-                                   visitedStyUris)
-        self._alt_base_uris = altBaseUris or []
-        self._ownerDoc = ownerDocument
-        return
-
-    def reset(self):
-        StylesheetHandler.reset(self)
-        self._ownerDoc = None
-        self._input_source = None
-        return
-
-    def clone(self):
-        return self.__class__(self._alt_base_uris, self._ownerDoc,
-                              self._import_index, self._global_vars,
-                              self._extElements, self._visited_stylesheet_uris)
 
     def fromInstant(self, instant, baseUri='', is_import=0):
@@ -179,5 +161,5 @@
         properties = []
         stylesheet = self._parseSrc(isrc, features, properties)
-        
+
         # Cache the string content for subsequent uses
         # e.g., xsl:import/xsl:include and document()
@@ -187,6 +169,4 @@
 
     def _parseSrc(self, isrc, features, properties):
-        self._input_source = isrc
-
         parser = Sax.CreateParser()
         parser.setContentHandler(self)
@@ -200,18 +180,16 @@
             parser.setProperty(propertyname, value)
 
+        prev_source = self._input_source
         try:
-            parser.parse(isrc)
-        except SAXParseException, e:
-            e = e.getException() or e
-            if isinstance(e, XsltException):
-                raise e
-            raise XsltException(Error.STYLESHEET_PARSE_ERROR, isrc.uri, e)
-        
-        self._input_source = None
-        
-        root = self._state_stack[0].node
-        if root is self._ownerDoc:
-            # the top-most stylesheet
-            root.stylesheet.setup()
+            self._input_source = isrc
+            try:
+                parser.parse(isrc)
+            except SAXParseException, e:
+                e = e.getException() or e
+                if isinstance(e, XsltException):
+                    raise e
+                raise XsltException(Error.STYLESHEET_PARSE_ERROR, isrc.uri, e)
+        finally:
+            self._input_source = prev_source
 
-        return root.stylesheet
+        return self._ownerDoc.stylesheet
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/StylesheetTree.py.diff?r1=1.23&r2=1.24
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/StylesheetTree.py?rev=1.24&content-type=text/vnd.viewcvs-markup

Index: StylesheetTree.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/StylesheetTree.py,v
retrieving revision 1.23
retrieving revision 1.24
diff -U2 -r1.23 -r1.24
--- StylesheetTree.py	11 May 2005 16:11:05 -0000	1.23
+++ StylesheetTree.py	27 Nov 2006 16:29:29 -0000	1.24
@@ -4,20 +4,12 @@
 Node classes for the stylesheet tree
 
-Copyright 2004 Fourthought, Inc. (USA).
+Copyright 2006 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
+
+from Ft.Xml.Xslt import XSL_NAMESPACE, ContentInfo
+
+__all__ = ['XsltNode', 'XsltRoot', 'XsltElement', 'XsltText']
 
 class XsltNode:
@@ -41,16 +33,15 @@
     doesPrime = False
     doesIdle = False
+    isPseudoNode = False
 
     def isLastChild(self):
         siblings = self.parent.children
-        if siblings.index(self) == len(siblings) - 1:
-            return 1
+        if siblings[-1] is self:
+            return True
         else:
-            isLast = 1
-            for node in siblings[siblings.index(self)+1:]:
-                if not node.isPseudoNode():
-                    isLast = 0
-                    break
-            return isLast
+            index = -1
+            while siblings[index].isPseudoNode:
+                index -= 1
+            return siblings[-1] is self
 
     def setup(self):
@@ -66,7 +57,4 @@
         return
 
-    def isPseudoNode(self):
-        return False
-
     def pprint(self, _indent=''):
         print _indent + str(self)
@@ -77,15 +65,25 @@
         return
 
+    def __str__(self):
+        ptr = id(self)
+        if ptr < 0:
+            ptr += 0x100000000
+        return "<%s at 0x%x>" % (self.__class__.__name__, ptr)
 
 class XsltRoot(XsltNode):
 
-    content = ContentInfo.Alt(ContentInfo.QName(XSL_NAMESPACE, 'xsl:stylesheet'),
-                              ContentInfo.QName(XSL_NAMESPACE, 'xsl:transform'),
-                              ContentInfo.ResultElements)
-
-    validator = ContentInfo.Validator(content)
+    content = ContentInfo.Alt(
+        ContentInfo.QName(XSL_NAMESPACE, 'xsl:stylesheet'),
+        ContentInfo.QName(XSL_NAMESPACE, 'xsl:transform'),
+        ContentInfo.ResultElements)
 
     nodeName = u'#document'
 
+    def children(self):
+        if self.stylesheet:
+            return (self.stylesheet,)
+        return ()
+    children = property(children)
+
     def __init__(self, baseUri):
         self.root = self
@@ -128,17 +126,10 @@
         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
+        assert not self.stylesheet
         self.stylesheet = child
-        self.children = [child]
-        return
-
-    def __str__(self):
-        return "<XsltRoot at 0x%x>" % id(self)
+        child.parent = self
 
 
@@ -146,13 +137,11 @@
 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):
+    def __init__(self, root, namespaceUri, localName, nodeName):
         self.root = root
-        self.baseUri = baseUri
         self.expandedName = (namespaceUri, localName)
+        self.nodeName = nodeName
         self.children = []
         self.attributes = {}
@@ -160,83 +149,38 @@
         return
 
-    def insertChild(self, index, child):
-        """INTERNAL USE ONLY"""
-        self.children.insert(index, child)
-        child.parent = self
-        if child.doesSetup:
-            child.setup()
-        return
-
-    def appendChild(self, child):
-        """INTERNAL USE ONLY"""
-        self.children.append(child)
-        child.parent = self
-        if child.doesSetup:
-            child.setup()
-        return
-
-    def parseAVT(self, avt):
-        """DEPRECATED: specify an attribute in 'legalAttrs' instead."""
-        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):
-        """DEPRECATED: specify an attribute in 'legalAttrs' instead."""
-        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):
-        """DEPRECATED: specify an attribute in 'legalAttrs' instead."""
-        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):
-        """DEPRECATED: specify an attribute in 'legalAttrs' instead."""
-        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):
-        """DEPRECATED: specify an attribute in 'legalAttrs' instead."""
-        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 appendChild(self, newChild):
+        assert self.parent is None
+        self.children.append(newChild)
+        newChild.parent = self
+        if newChild.doesSetup:
+            newChild.setup()
+        return
+
+    def insertChild(self, index, newChild):
+        assert self.parent is None
+        self.children.insert(index, newChild)
+        newChild.parent = self
+        if newChild.doesSetup:
+            newChild.setup()
+        return
+
+    def removeChild(self, oldChild):
+        assert self.parent is None
+        self.children.remove(oldChild)
+        oldChild.parent = None
+
+    def replaceChild(self, oldChild, newChild):
+        assert self.parent is None
+        index = self.children.index(oldChild)
+        oldChild.parent = None
+        self.children[index] = newChild
+        newChild.parent = self
+        if newChild.doesSetup:
+            newChild.setup()
+        return
+
+    def setAttribute(self, namespaceURI, localName, value):
+        assert self.parent is None
+        self.attributes[namespaceURI, localName] = value
 
     def instantiate(self, context, processor):
@@ -265,9 +209,11 @@
 
     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)
+        ptr = id(self)
+        if ptr < 0:
+            ptr += 0x100000000
+        return ("<%s at 0x%x: name %r, %d attributes, %d children,"
+                " precedence %d>") % (self.__class__.__name__, ptr,
+                                      self.nodeName, len(self.attributes),
+                                      len(self.children), self.importIndex)
 
 
@@ -276,7 +222,10 @@
     nodeName = u'#text'
 
-    def __init__(self, root, baseUri, data):
+    def baseUri(self):
+        return self.parent.baseUri
+    baseUri = property(baseUri)
+
+    def __init__(self, root, data):
         self.root = root
-        self.baseUri = baseUri
         self.data = data
         return
@@ -287,7 +236,11 @@
 
     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))
+        ptr = id(self)
+        if ptr < 0:
+            ptr += 0x100000000
+        data = self.data
+        if len(data) > 20:
+            data = data[:10] + '...' + data[-10:]
+        return "<%s at 0x%x: %r>" % (self.__class__.__name__, ptr, data)
+
+from cStylesheetTree import *
\ No newline at end of file
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/TemplateElement.py.diff?r1=1.11&r2=1.12
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/TemplateElement.py?rev=1.12&content-type=text/vnd.viewcvs-markup

Index: TemplateElement.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/TemplateElement.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -U2 -r1.11 -r1.12
--- TemplateElement.py	6 Apr 2005 23:05:47 -0000	1.11
+++ TemplateElement.py	27 Nov 2006 16:29:29 -0000	1.12
@@ -19,5 +19,5 @@
 
 class TemplateElement(XsltElement):
-    category = CategoryTypes.TOP_LEVEL_ELEMENT
+
     content = ContentInfo.Seq(
         ContentInfo.Rep(ContentInfo.QName(XSL_NAMESPACE, 'xsl:param')),
@@ -30,4 +30,15 @@
         }
 
+    doesSetup = True
+
+    def setup(self):
+        self._params = [ (child, child._name) for child in self.children
+                         if child.expandedName == (XSL_NAMESPACE, 'param') ]
+        if self._params:
+            self._instructions = self.children[len(self._params)+1:-1]
+        else:
+            self._instructions = self.children
+        return
+
     def getTemplateInfo(self, position):
         infos = []
@@ -68,23 +79,30 @@
 
     def instantiate(self, context, processor, params=None):
-        params = params or {}
+        if params is None:
+            params = {}
+
+        if self._params:
+            varBindings = context.varBindings
+            context.varBindings = varBindings.copy()
 
         while 1:
             context.recursiveParams = None
 
-            for child in self.children:
-                if child.expandedName == (XSL_NAMESPACE, 'param'):
-                    value = params.get(child._name)
-                    if value is not None:
-                        context.varBindings[child._name] = value
-                    else:
-                        child.instantiate(context, processor)
+            for child, param in self._params:
+                if param in params:
+                    context.varBindings[param] = params[param]
                 else:
                     child.instantiate(context, processor)
 
+            for child in self._instructions:
+                child.instantiate(context, processor)
+
             if context.recursiveParams is not None:
                 # Update the params from the values given in recursiveParams.
-                params.update(context.recursiveParams)
+                params = context.recursiveParams
             else:
                 break
+
+        if self._params:
+            context.varBindings = varBindings
         return
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/TextElement.py.diff?r1=1.6&r2=1.7
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/TextElement.py?rev=1.7&content-type=text/vnd.viewcvs-markup

Index: TextElement.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/TextElement.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -U2 -r1.6 -r1.7
--- TextElement.py	6 Apr 2005 23:05:47 -0000	1.6
+++ TextElement.py	27 Nov 2006 16:29:29 -0000	1.7
@@ -4,18 +4,13 @@
 Implementation of the xsl:text element.
 
-Copyright 2005 Fourthought, Inc. (USA).
+Copyright 2006 Fourthought, Inc. (USA).
 Detailed license and copyright information: http://4suite.org/COPYRIGHT
 Project home, documentation, distributions: http://4suite.org/
 """
 
-from xml.dom import Node
-
-from Ft.Xml import EMPTY_NAMESPACE
-from Ft.Xml.Xslt import XsltElement, XsltException, Error, XSL_NAMESPACE
-from Ft.Xml.Xslt import CategoryTypes, ContentInfo, AttributeInfo
-
+from Ft.Xml.Xslt import XsltElement, ContentInfo, AttributeInfo
 
 class TextElement(XsltElement):
-    category = CategoryTypes.INSTRUCTION
+
     content = ContentInfo.Text
     legalAttrs = {
@@ -23,11 +18,19 @@
         }
 
-    def instantiate(self, context, processor):
+    doesSetup = True
+
+    def setup(self):
         if self.children:
-            value = self.children[0].data
+            self._value = self.children[0].data
+        else:
+            self._value = None
+        return
+
+    def instantiate(self, context, processor):
+        if self._value:
             if self._disable_output_escaping:
-                processor.writers[-1].text(value, escapeOutput=False)
+                processor.writers[-1].text(self._value, False)
             else:
-                processor.writers[-1].text(value)
+                processor.writers[-1].text(self._value)
 
         return
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/UndefinedElements.py.diff?r1=1.3&r2=1.4
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/UndefinedElements.py?rev=1.4&content-type=text/vnd.viewcvs-markup

Index: UndefinedElements.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/UndefinedElements.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -U2 -r1.3 -r1.4
--- UndefinedElements.py	6 Apr 2005 23:05:47 -0000	1.3
+++ UndefinedElements.py	27 Nov 2006 16:29:29 -0000	1.4
@@ -12,4 +12,6 @@
 from Ft.Xml.Xslt import CategoryTypes, ContentInfo
 
+__all__ = ['UndefinedXsltElement', 'UndefinedExtensionElement']
+
 class _UndefinedElement(XsltElement):
 
@@ -49,3 +51,3 @@
                                     *self.expandedName)
 
-    
+
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ValueOfElement.py.diff?r1=1.8&r2=1.9
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/ValueOfElement.py?rev=1.9&content-type=text/vnd.viewcvs-markup

Index: ValueOfElement.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/ValueOfElement.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -U2 -r1.8 -r1.9
--- ValueOfElement.py	6 Apr 2005 23:05:47 -0000	1.8
+++ ValueOfElement.py	27 Nov 2006 16:29:29 -0000	1.9
@@ -9,7 +9,7 @@
 """
 
+from Ft.Xml.XPath import Conversions
 from Ft.Xml.Xslt import XsltElement
 from Ft.Xml.Xslt import CategoryTypes, ContentInfo, AttributeInfo
-from Ft.Xml.XPath import Conversions
 
 class ValueOfElement(XsltElement):
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/XPathExtensions.py.diff?r1=1.9&r2=1.10
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/XPathExtensions.py?rev=1.10&content-type=text/vnd.viewcvs-markup

Index: XPathExtensions.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/XPathExtensions.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -U2 -r1.9 -r1.10
--- XPathExtensions.py	6 Apr 2005 23:05:47 -0000	1.9
+++ XPathExtensions.py	27 Nov 2006 16:29:29 -0000	1.10
@@ -33,5 +33,5 @@
     def __str__(self):
         return "<SortedExpr at 0x%x: %s>" % (id(self), repr(self.expression))
-    
+
     def compare(self, (node1, keys1), (node2, keys2)):
         for i in xrange(len(self.cmps)):
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/XsltContext.py.diff?r1=1.14&r2=1.15
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/XsltContext.py?rev=1.15&content-type=text/vnd.viewcvs-markup

Index: XsltContext.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/XsltContext.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -U2 -r1.14 -r1.15
--- XsltContext.py	10 Dec 2005 17:54:49 -0000	1.14
+++ XsltContext.py	27 Nov 2006 16:29:29 -0000	1.15
@@ -4,19 +4,15 @@
 Context and state information for XSLT processing
 
-Copyright 2003 Fourthought, Inc. (USA).
+Copyright 2006 Fourthought, Inc. (USA).
 Detailed license and copyright information: http://4suite.org/COPYRIGHT
 Project home, documentation, distributions: http://4suite.org/
 """
 
-import Exslt, BuiltInExtFunctions
 from Ft.Lib.Uri import UriDict
-from Ft.Xml import EMPTY_NAMESPACE
-from Ft.Xml.XPath import Context, Util, RuntimeException
-from Ft.Xml.Xslt import XsltFunctions
+from Ft.Xml.Lib.XmlString import SplitQName
+from Ft.Xml.XPath import Context, RuntimeException
+from Ft.Xml.Xslt import XsltFunctions, BuiltInExtFunctions, Exslt
 
-
-#NOTE: Some of the state information maintained here would probably be better
-#managed by the processor, but until Python pre-GC support is phased out,
-#the current arrangement will have to do
+__all__ = ['XsltContext']
 
 class XsltContext(Context.Context):
@@ -27,28 +23,10 @@
     functions.update(BuiltInExtFunctions.ExtFunctions)
 
-    def __init__(self,
-                 node,
-                 position=1,
-                 size=1,
-                 currentNode=None,
-                 varBindings=None,
-                 processorNss=None,
-                 stylesheet=None,
-                 processor=None,
-                 mode=None,
-                 extModuleList = None,
-                 extFunctionMap = None,
-                 ):
-
-
-        Context.Context.__init__(self,
-                                 node,
-                                 position,
-                                 size,
-                                 varBindings,
-                                 processorNss,
-                                 extModuleList,
-                                 extFunctionMap
-                                 )
+    def __init__(self, node, position=1, size=1, currentNode=None,
+                 varBindings=None, processorNss=None, stylesheet=None,
+                 processor=None, mode=None, extModuleList=None,
+                 extFunctionMap=None):
+        Context.Context.__init__(self, node, position, size, varBindings,
+                                 processorNss, extModuleList, extFunctionMap)
         self.currentNode = currentNode
         self.stylesheet = stylesheet
@@ -56,5 +34,4 @@
         self.processor = processor
         self.documents = UriDict()
-        self.rtfs = []
         self.currentInstruction = None
         self.recursiveParams = None
@@ -67,24 +44,20 @@
         return
 
-    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):
-        if not qname: return None
-        prefix, local = self.splitQName(qname)
+    def splitQName(self, qualifiedName):
+        if not qualifiedName: return None
+        return SplitQName(qualifiedName)
+
+    def expandQName(self, qualifiedName):
+        if not qualifiedName: return None
+        prefix, local = SplitQName(qualifiedName)
         if prefix:
             try:
-                expanded = (self.processorNss[prefix], local)
+                namespace = self.processorNss[prefix]
             except KeyError:
-                raise RuntimeException(RuntimeException.UNDEFINED_PREFIX, prefix)
+                raise RuntimeException(RuntimeException.UNDEFINED_PREFIX,
+                                       prefix)
         else:
-            expanded = (EMPTY_NAMESPACE, local)
-        return expanded
+            namespace = None
+        return (namespace, local)
 
     def setProcessState(self, execNode):
@@ -94,16 +67,18 @@
 
     def clone(self):
-        return XsltContext(self.node, self.position, self.size,
-                           self.currentNode, self.varBindings.copy(),
-                           self.processorNss, self.stylesheet,
-                           self.processor, self.mode)
+        return self.__class__(node=self.node,
+                              position=self.position,
+                              size=self.size,
+                              currentNode=self.currentNode,
+                              varBindings=self.varBindings.copy(),
+                              processorNss=self.processorNss,
+                              stylesheet=self.stylesheet,
+                              processor=self.processor,
+                              mode=self.mode)
 
     def __repr__(self):
-        return '<XsltContext at %x: node %s, position %d, size %d, mode %r>' % (
-            id(self),
-            repr(self.node),
-            self.position,
-            self.size,
-            self.mode
-            )
-
+        ptr = id(self)
+        if ptr < 0:
+            ptr += 0x100000000
+        return ('<XsltContext at 0x%x: node %r, position %d, size %d, mode %r'
+                '>' % (ptr, self.node, self.position, self.size, self.mode))
ViewCVS diff:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/XsltFunctions.py.diff?r1=1.46&r2=1.47
ViewCVS view:
  http://cvs.4suite.org/viewcvs/4Suite/Ft/Xml/Xslt/XsltFunctions.py?rev=1.47&content-type=text/vnd.viewcvs-markup

Index: XsltFunctions.py
===================================================================
RCS file: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/XsltFunctions.py,v
retrieving revision 1.46
retrieving revision 1.47
diff -U2 -r1.46 -r1.47
--- XsltFunctions.py	2 Aug 2005 22:43:01 -0000	1.46
+++ XsltFunctions.py	27 Nov 2006 16:29:29 -0000	1.47
@@ -17,7 +17,5 @@
 from Ft.Xml.XPath.XPathTypes import NodesetType
 from Ft.Xml.Xslt import XsltRuntimeException, Error, XSL_NAMESPACE
-
-# is there no better list of implemented core XSLT elements?
-from Ft.Xml.Xslt.StylesheetHandler import _ELEMENT_MAPPING
+from Ft.Xml.Xslt.StylesheetHandler import ELEMENT_MAPPING
 
 def Document(context, object, nodeSet=None):
@@ -334,9 +332,9 @@
 
     if namespaceURI == XSL_NAMESPACE:
-        available = localName in _ELEMENT_MAPPING
-    elif namespaceURI == EMPTY_NAMESPACE:
-        available = False
-    else:
+        available = localName in ELEMENT_MAPPING
+    elif namespaceURI:
         available = expandedName in context.processor.extElements
+    else:
+        available = False
 
     return available and boolean.true or boolean.false


More information about the 4suite-checkins mailing list