########################################################################
#
# File Name:            SortElement.py
#
# Documentation:        http://docs.4suite.com/4XSLT/SortElement.py.html
#
"""
Implementation of the XSLT Spec sort stylesheet element.
WWW: http://4suite.com/4XSLT        e-mail: support@4suite.com

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

import string
from xml.dom import EMPTY_NAMESPACE
import xml.dom.ext
import xml.dom.Element
import xml.xslt
from xml.xslt import XsltElement, XsltException, Error, AttributeValueTemplate
from xml.xpath import XPathParser
from xml.xpath import Conversions

class SortElement(XsltElement):
    legalAttrs = ('select', 'lang', 'data-type', 'case-order', 'order')

    def __init__(self, doc, uri=xml.xslt.XSL_NAMESPACE, localName='sort', prefix='xsl', baseUri=''):
        XsltElement.__init__(self, doc, uri, localName, prefix, baseUri)

    def setup(self):
        self.__dict__['_select'] = self.getAttributeNS(EMPTY_NAMESPACE, 'select') or '.'
        data_type = self.getAttributeNS(EMPTY_NAMESPACE, 'data-type')
        self.__dict__['_data_type'] = data_type and AttributeValueTemplate.AttributeValueTemplate(data_type) or None
        case_order = self.getAttributeNS(EMPTY_NAMESPACE, 'case-order')
        self.__dict__['_case_order'] = case_order and AttributeValueTemplate.AttributeValueTemplate(case_order) or None
        order = self.getAttributeNS(EMPTY_NAMESPACE, 'order')
        self.__dict__['_order'] = order and AttributeValueTemplate.AttributeValueTemplate(order) or None
        self.__dict__['_nss'] = xml.dom.ext.GetAllNs(self)
        parser = XPathParser.XPathParser()
        self.__dict__['_expr'] = parser.parseExpression(self._select)
        return

    def instantiate(self, context, processor, nodeList=None, specList=None):
  
        if nodeList is None:
            nodeList = []
        if specList is None:
            specList = []

        origState = context.copy()
        context.setNamespaces(self._nss)

        if self._data_type:
            data_type = self._data_type.evaluate(context)
            if data_type not in ['text', 'number']:
                raise XsltException(Error.ILLEGAL_SORT_DATA_TYPE_VALUE)
        else:
            data_type = 'text'
        if self._case_order:
            case_order = self._case_order.evaluate(context)
            if case_order not in ['upper-first', 'lower-first']:
                raise XsltException(Error.ILLEGAL_SORT_CASE_ORDER_VALUE)
        else:
            case_order = 'lower-first'
        if self._order:
            order = self._order.evaluate(context)
            if order not in ['ascending', 'descending']:
                raise XsltException(Error.ILLEGAL_SORT_ORDER_VALUE)
        else:
            order = 'ascending'

        keys = []
        node_dict = {}
        pos = 1
        size = len(nodeList)
        tempState = context.copyNodePosSize()
        for node in nodeList:
            context.setNodePosSize((node,pos,size))
            result = self._expr.evaluate(context)
            key = Conversions.StringValue(result)
            if not key in keys:
                keys.append(key)
            if node_dict.has_key(key):
                node_dict[key].append(node)
            else:
                node_dict[key] = [node]
            pos = pos + 1
            context.setNodePosSize(tempState)

        keys.sort(lambda x, y, o=order, d=data_type, c=case_order: Cmp(x, y, o, d, c))
        sorted_list = []
        for key in keys:
            sub_list = node_dict[key]
            if len(sub_list) > 1 and specList:
                sub_list = specList[0].instantiate(context, processor, sub_list, specList[1:])[1]
            sorted_list = sorted_list + sub_list
        context.set(origState)
        return (context, sorted_list)

    def __getinitargs__(self):
        return (None, self.namespaceURI, self.localName, self.prefix,
                self.baseUri)

    def __getstate__(self):
         base_state = XsltElement.__getstate__(self)
         new_state = (base_state, self._nss, self._select, self._data_type,
                      self._case_order, self._order, self._expr)
         return new_state

    def __setstate__(self, state):
        XsltElement.__setstate__(self, state[0])
        self._nss = state[1]
        self._select = state[2]
        self._data_type = state[3]
        self._case_order = state[4]
        self._order = state[5]
        self._expr = state[6]
        return


def Cmp(a, b, order, dataType, caseOrder):
    if dataType == 'number':
        a = float(a or 0)
        b = float(b or 0)
    elif caseOrder == 'lower-first':
        if a: a = string.swapcase(a[0])+a[1:]
        if b: b = string.swapcase(b[0])+b[1:]
    if order == 'ascending':
        return cmp(a,b)
    else:
        return cmp(b,a)

