########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/ApplyTemplatesElement.py,v 1.10 2005-04-06 23:05:47 jkloth Exp $
"""
Implementation of xsl:apply-templates instruction

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

from Ft.Xml import EMPTY_NAMESPACE
from Ft.Xml.Xslt import XsltElement, XSL_NAMESPACE, XsltRuntimeException, Error
from Ft.Xml.Xslt import CategoryTypes, AttributeInfo, ContentInfo
from Ft.Xml.Xslt.XPathExtensions import SortedExpression

class ApplyTemplatesElement(XsltElement):

    category = CategoryTypes.INSTRUCTION
    content = ContentInfo.Rep(
        ContentInfo.Alt(ContentInfo.QName(XSL_NAMESPACE, 'xsl:sort'),
                        ContentInfo.QName(XSL_NAMESPACE, 'xsl:with-param'))
        )
    legalAttrs = {
        'select' : AttributeInfo.Expression(),
        'mode' : AttributeInfo.QName(),
        }

    doesSetup = 1

    def setup(self):
        sort_keys = []
        self._params = []
        for child in self.children:
            # only XSL children are guaranteed by the validation
            name = child.expandedName[1]
            if name == 'sort':
                sort_keys.append(child)
            elif name == 'with-param':
                self._params.append((child, child._name, child._select))

        if sort_keys:
            self._select = SortedExpression(self._select, sort_keys)
        return

    # hooks for Fourthought extension element (mode as AVT)

    def _instantiate_mode(self, context):
        return self._mode

    def instantiate(self, context, processor):
        context.processorNss = self.namespaces
        context.currentInstruction = self

        with_params = {}
        for (param, name, expr) in self._params:
            context.processorNss = param.namespaces
            context.currentInstruction = param
            with_params[name] = expr.evaluate(context)

        if self._select:
            node_set = self._select.evaluate(context)
            # it must really be a node-set, and if XSLT 1.0, not a result tree fragment
            if not isinstance(node_set, list):
                raise XsltRuntimeException(
                    Error.ILLEGAL_APPLYTEMPLATE_NODESET, self)
        else:
            node_set = context.node.childNodes

        # Iterate over the nodes
        state = context.copy()
        mode = context.mode
        context.mode = self._instantiate_mode(context)

        pos = 1
        size = len(node_set)
        for node in node_set:
            context.node, context.position, context.size = node, pos, size
            processor.applyTemplates(context, with_params)
            pos += 1

        context.mode = mode
        context.set(state)
        return
