/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2004 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#include "XalanEXSLTString.hpp"
#include "XalanEXSLTStringImpl.hpp"



#include <xalanc/PlatformSupport/DoubleSupport.hpp>
#include <xalanc/PlatformSupport/XalanUnicode.hpp>
#include <xalanc/PlatformSupport/XalanTranscodingServices.hpp>



#include <xalanc/DOMSupport/DOMServices.hpp>



#include <xalanc/XPath/XObjectFactory.hpp>
#include <xalanc/XPath/XPathEnvSupportDefault.hpp>



XALAN_CPP_NAMESPACE_BEGIN



const XalanDOMChar	XalanEXSLTFunctionAlign::s_centerString[] =
{
	XalanUnicode::charLetter_c,
	XalanUnicode::charLetter_e,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_t,
	XalanUnicode::charLetter_e,
	XalanUnicode::charLetter_r,
	0
};



const XalanDOMChar	XalanEXSLTFunctionAlign::s_rightString[] =
{
	XalanUnicode::charLetter_r,
	XalanUnicode::charLetter_i,
	XalanUnicode::charLetter_g,
	XalanUnicode::charLetter_h,
	XalanUnicode::charLetter_t,
	0
};



XObjectPtr
XalanEXSLTFunctionAlign::execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			const XObjectArgVectorType&		args,
			const LocatorType*				locator) const
{
	const XObjectArgVectorType::size_type	theSize = args.size();

	if (theSize != 2 && theSize != 3)
	{
		executionContext.error(getError(), context, locator);
	}

	assert(args[0].null() == false && args[1].null() == false && (theSize == 2 || args[2].null() == false));

	const XalanDOMString&	theTargetString = args[0]->str();
	const XalanDOMString&	thePaddingString = args[1]->str();

	const XalanDOMString::size_type		theTargetStringLength = theTargetString.length();
	const XalanDOMString::size_type		thePaddingStringLength = thePaddingString.length();

	if (theTargetStringLength == thePaddingStringLength)
	{
		return XObjectPtr(args[0]);
	}
	else
	{
		XPathExecutionContext::GetAndReleaseCachedString	theGuard(executionContext);

		XalanDOMString&		theResult = theGuard.get();

		if (theTargetStringLength > thePaddingStringLength)
		{
			theResult.assign(theTargetString, 0, thePaddingStringLength);
		}
		else
		{
			theResult.reserve(thePaddingStringLength);

			enum eAlignment { eCenter, eLeft, eRight };

			eAlignment	theAlignment = eLeft;

			if (theSize == 3)
			{
				const XalanDOMString&	theAlignmentString = args[2]->str();

				if (equals(
							s_centerString,
							theAlignmentString.c_str(),
							sizeof(s_centerString) / sizeof(s_centerString[0]) - 1) == true)
				{
					theAlignment = eCenter;
				}
				else if (equals(
							s_rightString,
							theAlignmentString.c_str(),
							sizeof(s_rightString) / sizeof(s_rightString[0]) - 1) == true)
				{
					theAlignment = eRight;
				}
			}

			if (theAlignment == eLeft)
			{
				theResult = theTargetString;

				theResult.append(thePaddingString, theTargetStringLength, thePaddingStringLength - theTargetStringLength);
			}
			else if (theAlignment == eRight)
			{
				theResult.assign(thePaddingString, 0, thePaddingStringLength - theTargetStringLength);
				theResult.append(theTargetString);
			}
			else if (theAlignment == eCenter)
			{
				const XalanDOMString::size_type		theStartIndex =
					(thePaddingStringLength - theTargetStringLength) / 2;

				theResult.assign(thePaddingString, 0, theStartIndex);

				theResult.append(theTargetString);

				theResult.append(
					thePaddingString,
					theTargetStringLength + theStartIndex,
					thePaddingStringLength - theTargetStringLength - theStartIndex);
			}
		}

		return executionContext.getXObjectFactory().createString(theResult);
	}
}



static const XalanDOMString		s_emptyString;



XObjectPtr
XalanEXSLTFunctionConcat::execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			const XObjectArgVectorType&		args,
			const LocatorType*				locator) const
{
	if (args.size() != 1)
	{
		executionContext.error(getError(), context, locator);
	}

	assert(args[0].null() == false);

	const NodeRefListBase&				theNodeSet = args[0]->nodeset();
	const NodeRefListBase::size_type	theLength = theNodeSet.getLength();

	if (theLength == 0)
	{
		return executionContext.getXObjectFactory().createStringReference(s_emptyString);
	}
	else
	{
		XPathExecutionContext::GetAndReleaseCachedString	theGuard(executionContext);

		XalanDOMString&		theResult = theGuard.get();

		for(NodeRefListBase::size_type i = 0; i < theLength; ++i)
		{
			assert(theNodeSet.item(i) != 0);

			DOMServices::getNodeData(*theNodeSet.item(i), theResult);
		}

		return executionContext.getXObjectFactory().createString(theResult);
	}
}



const XalanDOMChar	XalanEXSLTFunctionPadding::s_spaceString[] =
{
	XalanUnicode::charSpace,
	0
};



XObjectPtr
XalanEXSLTFunctionPadding::execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			const XObjectArgVectorType&		args,
			const LocatorType*				locator) const
{
	assert(m_space == s_spaceString);

	const XObjectArgVectorType::size_type	theSize = args.size();

	if (theSize != 1 && theSize != 2)
	{
		executionContext.error(getError(), context, locator);
	}

	assert(args[0].null() == false && (theSize == 1 || args[1].null() == false));

	const double						theLength = DoubleSupport::round(args[0]->num());
	const XalanDOMString&				thePaddingString = theSize == 2 ? args[1]->str() : m_space;
	const XalanDOMString::size_type		thePaddingStringLength = thePaddingString.length();

	if (theLength == 0.0 || thePaddingStringLength == 0)
	{
		return executionContext.getXObjectFactory().createStringReference(s_emptyString);
	}
	else
	{
		XPathExecutionContext::GetAndReleaseCachedString	theGuard(executionContext);

		XalanDOMString&		theResult = theGuard.get();

		if (thePaddingStringLength == 1)
		{
			theResult.assign(XalanDOMString::size_type(theLength), thePaddingString[0]);
		}
		else
		{
			XalanDOMString::size_type	theRemainingLength = XalanDOMString::size_type(theLength);

			for(;;)
			{
				if (theRemainingLength > thePaddingStringLength)
				{
					theResult.append(thePaddingString);

					theRemainingLength -= thePaddingStringLength;
				}
				else
				{
					theResult.append(
						thePaddingString,
						0,
						XalanDOMString::size_type(theRemainingLength));

					break;
				}
			}
		}

		return executionContext.getXObjectFactory().createString(theResult);
	}
}

// Reserved URI characters
const XalanDOMChar	
XalanEXSLTFunctionEncodeURI::s_reservedChars[] = 
{
	XalanUnicode::charDollarSign,
	XalanUnicode::charAmpersand,
	XalanUnicode::charPlusSign,
	XalanUnicode::charComma,
	XalanUnicode::charSolidus,
	XalanUnicode::charColon,
	XalanUnicode::charSemicolon,
	XalanUnicode::charEqualsSign,
	XalanUnicode::charQuestionMark,
	XalanUnicode::charCommercialAt,
	XalanUnicode::charLeftSquareBracket,
	XalanUnicode::charRightSquareBracket
};
const XalanDOMString::size_type 
XalanEXSLTFunctionEncodeURI::s_reservedCharsSize = sizeof(s_reservedChars) / sizeof(s_reservedChars[0]);

// Excluded URI characters
const XalanDOMChar
XalanEXSLTFunctionEncodeURI::s_excludedChars[] =
{
	XalanUnicode::charSpace,
	XalanUnicode::charQuoteMark,
	XalanUnicode::charNumberSign,
	XalanUnicode::charPercentSign,
	XalanUnicode::charLessThanSign,
	XalanUnicode::charGreaterThanSign,
	XalanUnicode::charReverseSolidus,
	XalanUnicode::charCircumflexAccent,
	XalanUnicode::charGraveAccent,
	XalanUnicode::charLeftCurlyBracket,
	XalanUnicode::charVerticalLine,
	XalanUnicode::charRightCurlyBracket
};
const XalanDOMString::size_type
XalanEXSLTFunctionEncodeURI::s_excludedCharsSize = sizeof(s_excludedChars) / sizeof(s_excludedChars[0]);

XObjectPtr
XalanEXSLTFunctionEncodeURI::execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			const XObjectArgVectorType&		args,
			const LocatorType*				locator) const
{
	const XObjectArgVectorType::size_type	theSize = args.size();

	if (theSize != 2 && theSize != 3)
	{
		executionContext.error(getError(), context, locator);
	}

	assert(args[0].null() == false && 
		   args[1].null() == false &&
		   (theSize == 3 || args[2].null() == false));

	const XalanDOMString& theString = args[0]->str();
	const bool            escapeReserved = args[1]->boolean();

	// We only support UTF-8, which is the default when there are only two arguments.
	const bool	fSupportedEncoding = theSize == 2 || XalanTranscodingServices::encodingIsUTF8(args[2]->str());

	if (theString.length() == 0 ||
		!fSupportedEncoding)
	{
		return executionContext.getXObjectFactory().createStringReference(s_emptyString);
	} 	
	
	XPathExecutionContext::GetAndReleaseCachedString theGuard(executionContext);
	XalanDOMString &theResult = theGuard.get();

	XALAN_USING_STD(find)

	for (XalanDOMString::size_type i = 0; i < theString.length(); ++i)
	{
		const XalanDOMChar	ch = theString[i];
		
		// ASCII characters (C0 control and basic Latin)
		if (ch <= 0x7F) {
			// Escape all control and excluded URI characters.
			// Reserved URI characters are optionally escaped.
			if (ch < 32 || ch > 126 || 
				find(s_excludedChars,s_excludedChars+s_excludedCharsSize,ch)
					!= s_excludedChars+s_excludedCharsSize || 
				(escapeReserved && 
				 find(s_reservedChars,s_reservedChars+s_reservedCharsSize,ch) 
					 != s_reservedChars+s_reservedCharsSize))
			{
				theResult+= escapedOctet(ch);
			}
			else 
			{
				theResult+= ch;
			}
		} 
		// Character is in the BMP
		else if(ch <= 0x7FF)
		{
			const XalanDOMChar	highByte = XalanDOMChar((ch >> 6) | 0xC0);
			const XalanDOMChar	lowByte = XalanDOMChar((ch & 0x3F) | 0x80);
			escapedOctet(highByte);
			theResult+= escapedOctet(highByte);
			theResult+= escapedOctet(lowByte);
		}
		// Character is in a higher supplementary plane
		else if((ch & 0xFC00) == 0xD800) // high surrogate
		{	
			const XalanDOMChar highSurrogate = XalanDOMChar((ch & 0x03FF) + 0x40);

			const XalanDOMChar nextChar = theString[++i];
			const XalanDOMChar lowSurrogate = XalanDOMChar(nextChar & 0x03FF);

			const XalanDOMChar byte1 = XalanDOMChar(0xF0 + ((highSurrogate & 0x0300) >> 8));
			const XalanDOMChar byte2 = XalanDOMChar(0x80 + ((highSurrogate & 0x00FC) >> 2));
			const XalanDOMChar byte3 = XalanDOMChar(0x80 + ((highSurrogate & 0x0003) << 4) + ((lowSurrogate & 0x03C0) >> 6));
			const XalanDOMChar byte4 = XalanDOMChar(0x80 + (lowSurrogate & 0x003F));

			theResult+= escapedOctet(byte1);
			theResult+= escapedOctet(byte2);
			theResult+= escapedOctet(byte3);
			theResult+= escapedOctet(byte4);
		}
		else
		{
			const XalanDOMChar	highByte = XalanDOMChar((ch >> 12) | 0xE0);
			const XalanDOMChar	middleByte = XalanDOMChar(((ch & 0x0FC0) >> 6) | 0x80);
			const XalanDOMChar	lowByte = XalanDOMChar((ch & 0x3F) | 0x80);
			theResult+= escapedOctet(highByte);
			theResult+= escapedOctet(middleByte);
			theResult+= escapedOctet(lowByte);
		}	
	}

	return executionContext.getXObjectFactory().createString(theResult);

}

const XalanDOMString
XalanEXSLTFunctionEncodeURI::escapedOctet(const XalanDOMChar	theChar) const
{
	XalanDOMString theResult;

	theResult += XalanUnicode::charPercentSign;
	XalanDOMString stringBuffer;
	UnsignedLongToHexDOMString(theChar, stringBuffer);
	if (stringBuffer.length() == 1) 
	{
		theResult += XalanUnicode::charDigit_0;
	}
	theResult += stringBuffer;
	return theResult;
}

const XalanDOMString::size_type XalanEXSLTFunctionDecodeURI::s_octetSize = 3;

XObjectPtr
XalanEXSLTFunctionDecodeURI::execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			const XObjectArgVectorType&		args,
			const LocatorType*				locator) const
{
	const XObjectArgVectorType::size_type	theSize = args.size();

	if (theSize != 1 && theSize != 2)
	{
		executionContext.error(getError(), context, locator);
	}

	assert(args[0].null() == false && 
		   (theSize == 2 || args[1].null() == false));

    const XalanDOMString& theString = args[0]->str();

	// We only support UTF-8, which is the default when there's only one argument.
	const bool	fSupportedEncoding = theSize == 1 || XalanTranscodingServices::encodingIsUTF8(args[1]->str());

	if (theString.length() == 0 || !fSupportedEncoding)
	{
		return executionContext.getXObjectFactory().createStringReference(s_emptyString);
	} 
		
	XPathExecutionContext::GetAndReleaseCachedString theGuard(executionContext);
	XalanDOMString &theResult = theGuard.get();

    for (XalanDOMString::size_type i = 0; i < theString.length(); ++i)
    {
		const XalanDOMChar	ch = theString[i];
			
		if (ch != XalanUnicode::charPercentSign) 
		{
			theResult+= ch;
			continue;
		}

		// escaped character

		// count number of escaped octets
		XalanDOMString::size_type numOctets = 0;
		XalanDOMString::size_type j = i;
		//assert(theString[j] == XalanUnicode::charPercentSign);
		//assert(j + 2 < theString.length());
		while (theString[j] == XalanUnicode::charPercentSign &&
			   j + 2 < theString.length()) 
		{
			++numOctets;
			j+= s_octetSize;
		}

		// no complete sequences found
		if (numOctets < 1) {
			break;
		}

		XalanDOMChar byte1 = hexCharsToByte(executionContext, context, locator,
										    theString[i+1],theString[i+2]);

		if ((byte1 & 0x80) == 0) 
		{
			i += 2;
		 	theResult+=byte1;
		}
		else if ((byte1 & 0xE0) == 0xC0 && numOctets >= 2) 
		{
			i += s_octetSize;
			XalanDOMChar byte2 = hexCharsToByte(executionContext, context, locator,
											    theString[i+1],theString[i+2]);
			i+=2;

			if (byte2 & 0x80 == 0) 
			{
				// invalid byte, bypass rest of this sequence
				i+= (numOctets - 2) * s_octetSize;
			}
			theResult+= XalanDOMChar(((byte1 & 0x1F) << 6) + (byte2 & 0x3F));
		} 
		else if ((byte1 & 0xF0) == 0xE0 && numOctets >= 3)
		{
			i+= s_octetSize;
			XalanDOMChar byte2 = hexCharsToByte(executionContext, context, locator,
											    theString[i+1],theString[i+2]);
			i+= s_octetSize;
			XalanDOMChar byte3 = hexCharsToByte(executionContext, context, locator,
											    theString[i+1],theString[i+2]);
			i+=2;
			
			if (byte2 & 0x80 == 0 || byte3 & 0x80 == 0)
			{
				// invalid byte, bypass rest of this sequence
				i+= (numOctets - 3) * s_octetSize;
			}
			theResult+= XalanDOMChar(((byte1 & 0x0F) << 12) + ((byte2 & 0x3F) << 6) + (byte3 & 0x3F));
		}
		else if ((byte1 & 0xF8) == 0xF0 && numOctets >=4)
		{
			i+= s_octetSize;
			XalanDOMChar byte2 = hexCharsToByte(executionContext, context, locator,
											    theString[i+1],theString[i+2]);
			i+= s_octetSize;
			XalanDOMChar byte3 = hexCharsToByte(executionContext, context, locator,
											    theString[i+1],theString[i+2]);
			i+= s_octetSize;
			XalanDOMChar byte4 = hexCharsToByte(executionContext, context, locator,
											    theString[i+1],theString[i+2]);
			i+=2;
	
			if (byte2 & 0x80 == 0 || byte3 & 0x80 == 0 || byte4 & 0x80 == 0)
			{
				// invalid byte, bypass rest of this sequence
				i+= (numOctets - 4) * s_octetSize;
			}
			theResult+= XalanDOMChar(0xD800 + 
				                     ((byte1 & 0x07) << 8) + ((byte2 & 0x3F) << 2) + 
				                     ((byte3 & 0x30) >> 4) - 0x40);
			theResult+= XalanDOMChar(0xDC00 + ((byte3 & 0x0F) << 6) + (byte4 & 0x3F));
		} 
		else 
		{
			// invalid byte, bypass rest of this sequence
			i+=numOctets * s_octetSize -1;
		}
	}
	return executionContext.getXObjectFactory().createString(theResult);
}

XalanDOMChar
XalanEXSLTFunctionDecodeURI::hexCharsToByte(
			XPathExecutionContext&		executionContext,
			XalanNode*					context,
			const LocatorType*			locator,
			const XalanDOMChar			highHexChar,
			const XalanDOMChar			lowHexChar) const
{
	XalanDOMChar byte = 0;
	
	XalanDOMChar curChar = lowHexChar;
	for (int place = 0; place < 2; ++place)
	{
		if (  curChar >= XalanUnicode::charDigit_0 
			&& curChar <= XalanUnicode::charDigit_9) // Digit 
		{
			byte += XalanDOMChar((curChar - XalanUnicode::charDigit_0) << (place * 4));
		}
		else if (   curChar >= XalanUnicode::charLetter_A 
			  	 && curChar <= XalanUnicode::charLetter_F) // Uppercase
		{
			byte += XalanDOMChar((curChar - XalanUnicode::charLetter_A + 10) << (place * 4));
		}
		else if (   curChar >= XalanUnicode::charLetter_a
				 && curChar <= XalanUnicode::charLetter_f)  // Lowercase
		{
			byte += XalanDOMChar((curChar - XalanUnicode::charLetter_a + 10) << place);
		}
		else 
		{
			executionContext.error("Invalid URI", context, locator);
		}
		curChar = highHexChar;
	}

	return byte;
}


static const XalanDOMChar	s_stringNamespace[] =
{
	XalanUnicode::charLetter_h,
	XalanUnicode::charLetter_t,
	XalanUnicode::charLetter_t,
	XalanUnicode::charLetter_p,
	XalanUnicode::charColon,
	XalanUnicode::charSolidus,
	XalanUnicode::charSolidus,
	XalanUnicode::charLetter_e,
	XalanUnicode::charLetter_x,
	XalanUnicode::charLetter_s,
	XalanUnicode::charLetter_l,
	XalanUnicode::charLetter_t,
	XalanUnicode::charFullStop,
	XalanUnicode::charLetter_o,
	XalanUnicode::charLetter_r,
	XalanUnicode::charLetter_g,
	XalanUnicode::charSolidus,
	XalanUnicode::charLetter_s,
	XalanUnicode::charLetter_t,
	XalanUnicode::charLetter_r,
	XalanUnicode::charLetter_i,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_g,
	XalanUnicode::charLetter_s,
	0
};



static const XalanDOMChar	s_alignFunctionName[] =
{
	XalanUnicode::charLetter_a,
	XalanUnicode::charLetter_l,
	XalanUnicode::charLetter_i,
	XalanUnicode::charLetter_g,
	XalanUnicode::charLetter_n,
	0
};



static const XalanDOMChar	s_concatFunctionName[] =
{
	XalanUnicode::charLetter_c,
	XalanUnicode::charLetter_o,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_c,
	XalanUnicode::charLetter_a,
	XalanUnicode::charLetter_t,
	0
};



static const XalanDOMChar	s_paddingFunctionName[] =
{
	XalanUnicode::charLetter_p,
	XalanUnicode::charLetter_a,
	XalanUnicode::charLetter_d,
	XalanUnicode::charLetter_d,
	XalanUnicode::charLetter_i,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_g,
	0
};

static const XalanDOMChar	s_encodeUriFunctionName[] =
{
	XalanUnicode::charLetter_e,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_c,
	XalanUnicode::charLetter_o,
	XalanUnicode::charLetter_d,
	XalanUnicode::charLetter_e,
	XalanUnicode::charHyphenMinus,
	XalanUnicode::charLetter_u,
	XalanUnicode::charLetter_r,
	XalanUnicode::charLetter_i,
	0
};

static const XalanDOMChar	s_decodeUriFunctionName[] =
{
	XalanUnicode::charLetter_d,
	XalanUnicode::charLetter_e,
	XalanUnicode::charLetter_c,
	XalanUnicode::charLetter_o,
	XalanUnicode::charLetter_d,
	XalanUnicode::charLetter_e,
	XalanUnicode::charHyphenMinus,
	XalanUnicode::charLetter_u,
	XalanUnicode::charLetter_r,
	XalanUnicode::charLetter_i,
	0
};

static const XalanEXSLTFunctionAlign	s_alignFunction;
static const XalanEXSLTFunctionConcat	s_concatFunction;
// Note this is a special constructor of XalanEXSLTFunctionPadding which
// allocates no memory.  It is only used here, so we can have table-based
// initialization, but not have any memory allocation.
static const XalanEXSLTFunctionPadding	s_paddingFunction(1);
static const XalanEXSLTFunctionEncodeURI s_encodeUriFunction;
static const XalanEXSLTFunctionDecodeURI s_decodeUriFunction;



static const XalanEXSLTStringFunctionsInstaller::FunctionTableEntry		theFunctionTable[] =
{
	{ s_alignFunctionName, &s_alignFunction },
	{ s_concatFunctionName, &s_concatFunction },
	{ s_paddingFunctionName, &s_paddingFunction },
	{ s_encodeUriFunctionName, &s_encodeUriFunction },
	{ s_decodeUriFunctionName, &s_decodeUriFunction },
	{ 0, 0 }
};



void
XalanEXSLTStringFunctionsInstaller::installLocal(XPathEnvSupportDefault&		theSupport)
{
	doInstallLocal(s_stringNamespace, theFunctionTable, theSupport);
}



void
XalanEXSLTStringFunctionsInstaller::installGlobal()
{
	doInstallGlobal(s_stringNamespace, theFunctionTable);
}



void
XalanEXSLTStringFunctionsInstaller::uninstallLocal(XPathEnvSupportDefault&	theSupport)
{
	doUninstallLocal(s_stringNamespace, theFunctionTable, theSupport);
}



void
XalanEXSLTStringFunctionsInstaller::uninstallGlobal()
{
	doUninstallGlobal(s_stringNamespace, theFunctionTable);
}



XALAN_CPP_NAMESPACE_END
