########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Xml/XPath/Context.py,v 1.11 2005/03/06 11:20:37 mbrown Exp $
"""
The context of an XPath expression

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

from types import ModuleType
import CoreFunctions, BuiltInExtFunctions
from Ft.Xml import XML_NAMESPACE

__all__ = ['Context']

class Context:
    functions = CoreFunctions.CoreFunctions.copy()
    functions.update(BuiltInExtFunctions.ExtFunctions)
    currentInstruction = None

    def __init__(self,
                 node,
                 position=1,
                 size=1,
                 varBindings=None,
                 processorNss=None,
                 extModuleList=None,
                 extFunctionMap=None):
        self.node = node
        self.position = position
        self.size = size
        self.varBindings = varBindings or {}
        self.processorNss = processorNss or {}
        self.processorNss.update({'xml': XML_NAMESPACE})

        self._documentIndex = {}

        # This may get mutated during processing
        functions = self.functions.copy()

        # Search the extension modules for defined functions
        if extModuleList:
            for module in extModuleList:
                if module:
                    if not isinstance(module, ModuleType):
                        module = __import__(module, {}, {}, ['ExtFunctions'])

                    if hasattr(module, 'ExtFunctions'):
                        functions.update(module.ExtFunctions)

        # Add functions given directly
        if extFunctionMap:
            functions.update(extFunctionMap)
        self.functions = functions
        return

    def __repr__(self):
        return "<Context at 0x%x: Node=%s, Postion=%d, Size=%d>" % (
            id(self), self.node, self.position, self.size)

    def addFunction(self, expandedName, function):
        if not callable(function):
            raise TypeError("function must be a callable object")
        self.functions[expandedName] = function
        return

    # Used for SortDocOrder with a nodeset containing multiple documents
    def addDocument(self, document):
        if document not in self._documentIndex:
            self._documentIndex[document] = len(self._documentIndex)
        return

    # Used for SortDocOrder with a nodeset containing multiple documents
    def compareDocuments(self, a, b):
        # Make sure the documents we are comparing have an index
        a = self._documentIndex.setdefault(a, len(self._documentIndex))
        b = self._documentIndex.setdefault(b, len(self._documentIndex))
        return cmp(a, b)

    def copy(self):
        return (self.node, self.position, self.size)

    def set(self, state):
        self.node, self.position, self.size = state
        return

    def clone(self):
        newobj = self.__class__(self, self.node, self.position, self.size)
        newobj.varBindings = self.varBindings.copy()
        newobj.processorNss = self.processorNss.copy()
        return newobj

