########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/GenericVariable.py,v 1.25 2005-04-06 23:05:47 jkloth Exp $
"""
Base implementation of XSLT variable assigning elements

Copyright 2002 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

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.StylesheetTree import XsltNode

class GenericVariableElement(XsltElement):

    category = None
    content = ContentInfo.Template
    legalAttrs = {
        'name' : AttributeInfo.QName(required=1),
        'select' : AttributeInfo.Expression(),
        'f:node-set' : AttributeInfo.YesNoAvt(default='no'),
        }

    doesSetup = True

    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:
            result = self._select.evaluate(context)
        elif self.children:
            #This used to be a try with the popResult() in the finally.
            #But this would cause masking of underlying exceptions triggered
            #in variable bodies. See
            #http://lists.fourthought.com/pipermail/4suite-dev/2003-March/001236.html
            processor.pushResultTree(self.baseUri)
            for child in self.children:
                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""
        context.varBindings[self._name] = result

        return

# pseudo-nodes for save/restore of variable bindings
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

    def prime(self, processor, context):
        if not self._is_primed:
            self.parent.children.append(self.popNode)
            self._is_primed = True
        return

    def instantiate(self, context, processor):
        self.savedVariables.append(context.varBindings)
        context.varBindings = context.varBindings.copy()
        return

    def isPseudoNode(self):
        return True


class PopVariablesNode(XsltNode):

    def __init__(self, savedVariables):
        self.savedVariables = savedVariables
        return

    def instantiate(self, context, processor):
        context.varBindings = self.savedVariables.pop()
        return

    def isPseudoNode(self):
        return True
