########################################################################
#
# File Name:            XPatterns.py
#
# Documentation:        http://docs.4suite.org/4XSLT/XPatterns.py.html
#

"""
IMplement Patterns according to the XSLT spec
WWW: http://4suite.org/4XSLT        e-mail: support@4suite.org

Copyright (c) 1999-2001 Fourthought Inc, USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""

from xml.dom import Node
import UserList
class Patterns:
    def __init__(self, patterns):
        self.patterns = patterns

    def getMatchShortcuts(self):
        return map(lambda p: p.getShortcut(), self.patterns)

    def match(self, context, node):
        for pattern in self.patterns:
            if pattern.match(context, node):
                return 1
        return 0

    def pprint(self, indent=''):
        print indent + str(self)
        for pattern in self.patterns:
            pattern.pprint(indent + '  ')
        return

    def __str__(self):
        return '<Patterns at %x: %s>' % (id(self), repr(self))

    def __repr__(self):
        result = repr(self.patterns[0])
        for pattern in self.patterns[1:]:
            result = result + ' | ' + repr(pattern)
        return result

class Pattern:
    def __init__(self, steps):
        # The steps are already in reverse order
        self.steps = steps
        self.priority = 0.5
        return

    def getShortcut(self):
        # A shortcut is (pattern, (pattern, extra_arg))
        if len(self.steps) == 1:
            (axis_type, node_test, ancestor) = self.steps[0]
            shortcut = (node_test, (node_test, axis_type))
        else:
            shortcut = (self, (self, None))
        return shortcut

    def match(self, context, node, dummy=None):
        (axis_type, node_test, ancestor) = self.steps[0]
        if not node_test.match(context, node, axis_type):
            return 0
        for (axis_type, node_test, ancestor) in self.steps[1:]:
            # Move up the tree
            if axis_type == Node.ATTRIBUTE_NODE:
                node = node.ownerElement
            else:
                node = node.parentNode
            if ancestor:
                while node:
                    if node_test.match(context, node, axis_type):
                        break
                    if axis_type == Node.ATTRIBUTE_NODE:
                        node = node.ownerElement
                    else:
                        node = node.parentNode
                else:
                    # We made it to the document without a match
                    return 0
            elif node is None: return 0
            elif not node_test.match(context, node, axis_type):
                return 0
        return 1

    def pprint(self, indent=''):
        print indent + str(self)

    def __str__(self):
        return '<Pattern at %x: %s>' % (id(self), repr(self))
    
    def __repr__(self):
        result = ''
        for (axis, test, ancestor) in self.steps:
            if axis == Node.ATTRIBUTE_NODE:
                step = '@' + repr(test)
            else:
                step = repr(test)
            result = step + (ancestor and '//' or '/') + result
        # remove trailing slash
        return result[:-1]


class PredicatedNodeTest:
    def __init__(self, nodeTest, predicateList):
        self.nodeTest = nodeTest
        self.predicates = predicateList
        self.priority = 0.5

    def match(self, context, node, principalType):
        if node.parentNode:
            node_set = node.parentNode.childNodes
        elif principalType == Node.ATTRIBUTE_NODE:
            node_set = node.ownerElement.attributes.values()
        else:
            # Must be a document
            return 0

        # Pass through the NodeTest
        node_set = filter(lambda node,
                          match=self.nodeTest.match,
                          context=context,
                          principalType=principalType:
                          match(context, node, principalType),
                          node_set)

        # Child and attribute axes are forward only
        node_set = self.predicates.filter(node_set, context, reverse=0)
        return node in node_set

    def __str__(self):
        return '<%s at %x: %s>' % (
            self.__class__.__name__,
            id(self),
            repr(self))

    def __repr__(self):
        return repr(self.nodeTest) + repr(self.predicates)

class DocumentNodeTest:
    def __init__(self):
        self.priority = 0.5

    def match(self, context, node, principalType):
        return node.nodeType == Node.DOCUMENT_NODE

    def __str__(self):
        return '<%s at %x: %s>' % (
            self.__class__.__name__,
            id(self),
            repr(self))

    def __repr__(self):
        return '/'

class IdKeyNodeTest:
    def __init__(self, idOrKey):
        self.priority = 0.5
        self.idOrKey = idOrKey

    def match(self, context, node, principalType):
        return node in self.idOrKey.evaluate(context)

    def __str__(self):
        return '<%s at %x: %s>' % (
            self.__class__.__name__,
            id(self),
            repr(self))

    def __repr__(self):
        return repr(self.idOrKey)

