########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Xml/Xslt/AttributeValueTemplate.py,v 1.9 2005/03/20 05:03:14 jkloth Exp $
"""
Implementation of XSLT attribute value templates

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

import re
from Ft.Xml import XPath
from Ft.Xml.Lib.XmlString import IsXmlSpace
from Ft.Xml.Xslt import XsltException, Error
from Ft.Xml.XPath import Conversions
from Ft.Xml.XPath import parser as XPathParser

g_braceSplitPattern = re.compile(r'([\{\}])')
g_parser = XPathParser.new()

class AttributeValueTemplate:
    def __init__(self, source, validator=None, element=None):
        self.source = source
        self.validator = validator
        self.element = element
        self._plainParts = plain = []
        self._parsedParts = parsed = []

        parser = XPathParser.new()
        curr_plain_part = ''
        curr_template_part = ''
        in_plain_part = 1
        split_form = g_braceSplitPattern.split(source)
        if len(split_form) == 1:
            self._plainParts = [source]
            return

        skip_flag = 0
        for i in xrange(len(split_form)):
            if skip_flag:
                skip_flag -= 1
                continue
            segment = split_form[i]
            if segment in ('{', '}'):
                #Here we are accounting for a possible blank segment in between
                try:
                    next = split_form[i + 1] + split_form[i + 2]
                except IndexError:
                    next = None
                if next == segment:
                    if in_plain_part:
                        curr_plain_part = curr_plain_part + segment
                    else:
                        curr_template_part = curr_template_part + segment
                    skip_flag = 2
                elif segment == '{':
                    if in_plain_part:
                        plain.append(curr_plain_part)
                        in_plain_part = 0

                        curr_plain_part = ''
                    else:
                        raise XsltException(Error.AVT_SYNTAX)
                else:
                    if not in_plain_part:
                        # may throw a SyntaxError
                        if IsXmlSpace(curr_template_part):
                            raise XsltException(Error.AVT_EMPTY)
                        parsed_part = parser.parse(curr_template_part)
                        parsed.append(parsed_part)
                        in_plain_part = 1
                        curr_template_part = ''
                    else:
                        raise XsltException(Error.AVT_SYNTAX)
            else:
                if in_plain_part:
                    curr_plain_part = curr_plain_part + segment
                else:
                    curr_template_part = curr_template_part + segment
        if in_plain_part:
            plain.append(curr_plain_part)
        else:
            raise XsltException(Error.AVT_SYNTAX)
        return

    def isConstant(self):
        return not self._parsedParts

    def evaluate(self, context):
        if not self.element and hasattr(context, 'currentInstruction'):
            self.element = context.currentInstruction
        if not self._parsedParts:
            return self.source
        result = ''
        expansions = map(lambda x, conv=Conversions.StringValue, ctx=context:
                         conv(x.evaluate(ctx)),
                         self._parsedParts)
        for i in xrange(len(expansions)):
            result = result + self._plainParts[i] + expansions[i]
        result = result + self._plainParts[-1]
        if self.validator:
            return self.validator.reprocess(self.element, result)
        else:
            return result

    def __nonzero__(self):
        return self.source is not None

