/*
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Class header file.
#include "DOMStringHelper.hpp"



#include <cassert>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>



#include <algorithm>



#if defined(XALAN_CLASSIC_IOSTREAMS)
#include <iostream.h>
#else
#include <iostream>
#endif



// Xerces header files
#include <xercesc/util/XMLString.hpp>

#if !defined(XML_LSTRSUPPORT)
#include <xercesc/util/PlatformUtils.hpp>
#endif



#include <xalanc/Include/STLHelper.hpp>



#include "DoubleSupport.hpp"
#include "XalanOutputStream.hpp"
#include "XalanUnicode.hpp"



XALAN_CPP_NAMESPACE_BEGIN



// The maximum number of digits that sprintf can put in a buffer.
// 100 for now.  We're using this because we want to avoid transcoding
// number strings when we don't have to,
const size_t	MAX_PRINTF_DIGITS = 100;

// The maximum number of characters for a floating point number.
const size_t	MAX_FLOAT_CHARACTERS = 100;



static XalanDOMChar		theNaNString[] =
{
	XalanUnicode::charLetter_N,
	XalanUnicode::charLetter_a,
	XalanUnicode::charLetter_N,
	0
};

static const XalanDOMChar	theNegativeInfinityString[] =
{
	XalanUnicode::charHyphenMinus,
	XalanUnicode::charLetter_I,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_f,
	XalanUnicode::charLetter_i,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_i,
	XalanUnicode::charLetter_t,
	XalanUnicode::charLetter_y,
	0
};

static const XalanDOMChar	thePositiveInfinityString[] =
{
	XalanUnicode::charLetter_I,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_f,
	XalanUnicode::charLetter_i,
	XalanUnicode::charLetter_n,
	XalanUnicode::charLetter_i,
	XalanUnicode::charLetter_t,
	XalanUnicode::charLetter_y,
	0
};

static const XalanDOMChar	theZeroString[] =
{
	XalanUnicode::charDigit_0,
	0
};



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString::size_type)
indexOf(
			const XalanDOMChar*			theString,
			XalanDOMString::size_type	theStringLength,
			const XalanDOMChar*			theSubstring,
			XalanDOMString::size_type	theSubstringLength)
{
	assert(theString != 0);
	assert(theSubstring != 0);

	// If the substring is longer than the string, then
	// it's not a substring.
	if (theStringLength < theSubstringLength)
	{
		return theStringLength;
	}
	else
	{
		bool						fMatch = false;

		XalanDOMString::size_type	theStringIndex = 0;

		// While we haven't matched, and we haven't finished with the
		// first string, and the number of characters left in the first
		// string is greater than the length of the second string, try
		// to match the strings.
		while(fMatch == false &&
			  theStringIndex < theStringLength &&
			  theStringLength - theStringIndex >= theSubstringLength)
		{
			// We always start over from the beginning of the second string.
			XalanDOMString::size_type	theSubstringIndex = 0;

			// This variable will be incremented to index into the first
			// string.  That way, we preserve the first string index for
			// when we have to restart the following loop with the next
			// position in the first string.
			XalanDOMString::size_type	theOffset = 0;

			// Compare the characters in the two strings, at the
			// current indices, until the characters don't match.
			while(theStringIndex < theStringLength &&
				  theSubstringIndex < theSubstringLength &&
				  theString[theStringIndex + theOffset] ==
						theSubstring[theSubstringIndex])
			{
				theOffset++;
				theSubstringIndex++;
			}

			// If we've reached the end of the second string,
			// then we've found a match.
			if (theSubstringIndex == theSubstringLength)
			{
				fMatch = true;
			}
			else
			{
				theStringIndex++;
			}
		}

		return fMatch == false ? theStringLength : theStringIndex;
	}
}


// $$$ ToDo:  This should be inlined by the 1.6 release...
XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString::size_type)
indexOf(
			const XalanDOMChar*		theString,
			const XalanDOMChar*		theSubstring)
{
	assert(theString != 0 && theSubstring != 0);

	return indexOf(theString, length(theString), theSubstring, length(theSubstring));
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString::size_type)
indexOf(
			const XalanDOMString&	theString,
			const XalanDOMString&	theSubstring)
{
	if (isEmpty(theString) == true)
	{
		return 0;
	}
	else if (isEmpty(theSubstring) == true)
	{
		return theString.length();
	}
	else
	{
		return indexOf(c_wstr(theString), c_wstr(theSubstring));
	}
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString::size_type)
lastIndexOf(
			const XalanDOMChar*		theString,
			XalanDOMChar			theChar)
{
	const XalanDOMString::size_type		theLength = length(theString);

	if (theLength == 0)
	{
		return theLength;
	}
	else
	{
		XalanDOMString::size_type	theIndex = theLength;

		while(theIndex > 0 && theString[theIndex - 1] != theChar)
		{
			theIndex--;
		}

		return theIndex == 0 ? theLength : theIndex - 1;
	}
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(bool)
startsWith(
			const XalanDOMChar*			theString,
			XalanDOMString::size_type	theStringLength,
			const XalanDOMChar*			theSubstring,
			XalanDOMString::size_type	theSubstringLength)
{
	if (theSubstringLength == 0)
	{
		// Make this work like Java...
		return true;
	}
	else if (theStringLength < theSubstringLength)
	{
		return false;
	}
	else
	{
		XalanDOMString::size_type	i = 0;

		// Compare each character...
		for (;
				i < theSubstringLength &&
						theString[i] == theSubstring[i];
					++i)
		{
			;
		}

		// If we've gotten to the end of the substring, then
		// return true.
		if (i == theSubstringLength)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(bool)
endsWith(
			const XalanDOMChar*			theString,
			XalanDOMString::size_type	theStringLength,
			const XalanDOMChar*			theSubstring,
			XalanDOMString::size_type	theSubstringLength)
{
	assert(theString != 0);
	assert(theSubstring != 0);

	bool				fResult = false;

	// If the substring is longer, there's no point in continuing.
	if (theSubstringLength >  0 && theStringLength >= theSubstringLength)
	{
		XalanDOMString::size_type	i = theStringLength;
		XalanDOMString::size_type	j = theSubstringLength;

		// Compare each character...
		for (;
				j > 0 &&
						theString[i - 1] == theSubstring[j - 1];
					--j, --i)
		{
			;
		}

		// If we've gotten to the beginning of the substring, then
		// return true.
		if (j == 0)
		{
			fResult = true;
		}
	}

	return fResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(void)
OutputString(XalanOutputStream&		theStream,
			 const CharVectorType&	theString)
{
	if (theString.empty() == false)
	{
		theStream.write(c_str(theString));
	}
}



XALAN_USING_STD(ostream)

XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(void)
OutputString(
			ostream&				theStream,
			const CharVectorType&	theString)
{
	if (theString.empty() == false)
	{
		theStream.write(&*theString.begin(), theString.size());
	}
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(void)
OutputString(XalanOutputStream&		theStream,
			 const XalanDOMChar*	theString)
{
	if (theString != 0)
	{
		theStream.write(theString);
	}
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(void)
OutputString(
			ostream&				theStream,
			const XalanDOMChar*		theString,
            MemoryManager&          theMemoryManager)
{
    CharVectorType  theVector(theMemoryManager);

    TranscodeToLocalCodePage(theString, theVector, false, '?');

	OutputString(theStream, theVector);
}




XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
substring(
			const XalanDOMChar*			theString,
			XalanDOMString&				theSubstring,
			XalanDOMString::size_type	theStartIndex,
			XalanDOMString::size_type	theEndIndex)
{
	assert(theString != 0);

	const XalanDOMString::size_type		theStringLength = length(theString);

	// $$$ ToDo: In Java-land, any failing of this
	// assertion would result in an exception being thrown.
	assert(theStartIndex <= theStringLength);

	if (theStartIndex == theStringLength)
	{
		// This is allowed, and should return an empty string.
		clear(theSubstring);
	}
	else
	{
		const XalanDOMString::size_type		theLength = theEndIndex == XalanDOMString::npos ? theStringLength - theStartIndex :
													theEndIndex - theStartIndex;
		assert(theStartIndex + theLength <= theStringLength);

		theSubstring.assign(theString + theStartIndex, theLength);
	}

	return theSubstring;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(void)
substring(
			const XalanDOMString&		theString,
			XalanDOMString&				theSubstring,
			XalanDOMString::size_type	theStartIndex,
			XalanDOMString::size_type	theEndIndex)
{
	const XalanDOMString::size_type		theStringLength = length(theString);

	// $$$ ToDo: In Java-land, any failing of this
	// assertion would result in an exception being thrown.
	assert(theStartIndex <= theStringLength);

	if (theStartIndex == theStringLength)
	{
		// This is allowed, and should return an empty string.
		clear(theSubstring);
	}
	else
	{
		const XalanDOMString::size_type		theLength = theEndIndex == XalanDOMString::npos ? theStringLength - theStartIndex :
													theEndIndex - theStartIndex;

		if (theLength == 0)
		{
			clear(theSubstring);
		}
		else
		{
			assert(theStartIndex + theLength <= theStringLength);

			theString.substr(theSubstring, theStartIndex, theLength);
		}
	}
}


template <class InputIteratorType, class OutputIteratorType, class FunctionType>
OutputIteratorType
TransformString(
			InputIteratorType	theInputBegin,
			InputIteratorType	theInputEnd,
			OutputIteratorType	theOutputIterator,
			FunctionType		theFunction)
{
#if defined(XALAN_NO_ALGORITHMS_WITH_BUILTINS)
	return XalanTransform(
			theInputBegin,
			theInputEnd,
			theOutputIterator,
			theFunction);
#else
	return XALAN_STD_QUALIFIER transform(
			theInputBegin,
			theInputEnd,
			theOutputIterator,
			theFunction);
#endif
}



template <class FunctionType>
XalanDOMString&
TransformString(
			const XalanDOMChar*			theInputString,
			XalanDOMString::size_type	theInputStringLength,
			FunctionType				theFunction,
            XalanDOMString&             theConvertedString)
{
	assert(theInputString != 0);

	TransformString(
			theInputString,
			theInputString + theInputStringLength,
			XALAN_STD_QUALIFIER back_inserter(theConvertedString),
			theFunction);

	return theConvertedString;
}



template <class FunctionType>
XalanDOMString&
TransformXalanDOMString(
			const XalanDOMString&	theInputString,
			FunctionType			theFunction,
            XalanDOMString&         theResult)
{
	const XalanDOMString::size_type		theStringLength = length(theInputString);

	if (theStringLength == 0)
	{
        theResult = theInputString;
	}
	else
	{
		const XalanDOMChar* const	theBuffer = c_wstr(theInputString);
		assert(theBuffer != 0);

        TransformString(theBuffer, theStringLength, theFunction, theResult);	
	}

    return theResult;
}



template <class FunctionType>
XalanDOMString&
TransformString(
			FunctionType		theFunction,
            XalanDOMString&     theString)
{
	TransformString(
			theString.begin(),
			theString.end(),
			theString.begin(),
			theFunction);

	return theString;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
toLowerCaseASCII(
            const XalanDOMChar*     theString,
            XalanDOMString&         theResult)
{
    TransformString(theString, length(theString), toLowerASCII, theResult);

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
toLowerCaseASCII(
            const XalanDOMString&	theString,
            XalanDOMString&         theResult)
{
    TransformXalanDOMString(theString, toLowerASCII, theResult);

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
toLowerCaseASCII(XalanDOMString&    theString)
{
    TransformString(toLowerASCII, theString);

	return theString;
}




XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
toUpperCaseASCII(
            const XalanDOMChar*     theString,
            XalanDOMString&         theResult)
{
    TransformString(theString, length(theString), toUpperASCII, theResult);

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
toUpperCaseASCII(
            const XalanDOMString&	theString,
            XalanDOMString&         theResult)
{
    TransformXalanDOMString(theString, toUpperASCII, theResult);

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
toUpperCaseASCII(XalanDOMString&    theString)
{
    TransformString(toUpperASCII, theString);

	return theString;
}



template <class InputCharType, class OutputCharType>
class IdentityTransform
{
public:

	OutputCharType
	operator()(InputCharType	theChar) const
	{
		return OutputCharType(theChar);
	}
};



template<class InputCharType, class OutputCharType>
IdentityTransform<InputCharType, OutputCharType>
makeIdentityTransform(
			const InputCharType*,
			const OutputCharType*)
{
	return IdentityTransform<InputCharType, OutputCharType>();
}



IdentityTransform<char, char>
makeCharIdentityTransform()
{
	char	theDummy;

	return makeIdentityTransform(&theDummy, &theDummy);
}


IdentityTransform<XalanDOMChar, XalanDOMChar>
makeXalanDOMCharIdentityTransform()
{
	XalanDOMChar	theDummy;

	return makeIdentityTransform(&theDummy, &theDummy);
}


template <class Type, class SizeType, class FunctionType>
int
doCompare(
			const Type*		theLHS,
			SizeType		theLHSLength,
			const Type*		theRHS,
			SizeType		theRHSLength,
			FunctionType	theTransformFunction)
{
	// We don't really have to order, so save some time...
	if (theLHSLength < theRHSLength)
	{
		return -1;
	}
	else if (theRHSLength < theLHSLength)
	{
		return 1;
	}
	else
	{
		Type	theLHSChar = Type(0);
		Type	theRHSChar = Type(0);

		for(SizeType i = 0; i < theLHSLength; i++)
		{
			theLHSChar = theTransformFunction(theLHS[i]);
			theRHSChar = theTransformFunction(theRHS[i]);

			if (theLHSChar != theRHSChar)
			{
				break;
			}
		}

		return int(theLHSChar - theRHSChar);
	}
}



template <class Type, class SizeType, class FunctionType>
int
doCollationCompare(
			const Type*		theLHS,
			SizeType		theLHSLength,
			const Type*		theRHS,
			SizeType		theRHSLength,
			FunctionType	theTransformFunction)
{
	int		theResult = 0;

	if (theLHSLength != 0 || theRHSLength != 0)
	{
		Type		theLHSChar = Type(0);
		Type		theRHSChar = Type(0);

		SizeType	i = 0;

		for(; i < theLHSLength && i < theRHSLength; i++)
		{
			theLHSChar = theTransformFunction(theLHS[i]);
			theRHSChar = theTransformFunction(theRHS[i]);

			if (theLHSChar != theRHSChar)
			{
				break;
			}
		}

		if (i == theLHSLength)
		{
			// We reached the end of theLHS...
			if (i != theRHSLength)
			{
				// but not the end of theRHS.
				theResult = -1;
			}
		}
		else if (i == theRHSLength)
		{
			// We reached the end of theRHS string...
			if (i != theLHSLength)
			{
				// but not the end of theLHS string.
				theResult = 1;
			}
		}
		else
		{
			// We didn't reach the end of _either_ string, so
			// return the difference between the two characters
			// that caused the problem.
			theResult = theLHSChar - theRHSChar;
		}
	}

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(int)
compare(
			const CharVectorType&	theLHS,
			const CharVectorType&	theRHS)
{
	return doCompare(
				toCharArray(theLHS),
				theLHS.size(),
				toCharArray(theRHS),
				theRHS.size(),
				makeCharIdentityTransform());
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(int)
compare(
			const XalanDOMChar*			theLHS,
			XalanDOMString::size_type	theLHSLength,
			const XalanDOMChar*			theRHS,
			XalanDOMString::size_type	theRHSLength)
{
	return doCompare(
				theLHS,
				theLHSLength,
				theRHS,
				theRHSLength,
				makeXalanDOMCharIdentityTransform());
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(int)
compareIgnoreCaseASCII(
			const XalanDOMChar*			theLHS,
			XalanDOMString::size_type	theLHSLength,
			const XalanDOMChar*			theRHS,
			XalanDOMString::size_type	theRHSLength)
{
	return doCompare(
				theLHS,
				theLHSLength,
				theRHS,
				theRHSLength,
				toUpperASCII);
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(int)
collationCompare(
			const XalanDOMChar*			theLHS,
			XalanDOMString::size_type	theLHSLength,
			const XalanDOMChar*			theRHS,
			XalanDOMString::size_type	theRHSLength)
{
	return doCollationCompare(
				theLHS,
				theLHSLength,
				theRHS,
				theRHSLength,
				makeXalanDOMCharIdentityTransform());
}



template <class Type, class SizeType, class FunctionType>
bool
doEquals(
			const Type*		theLHS,
			const Type*		theRHS,
			SizeType		theLength,
			FunctionType	theTransformFunction)
{
	assert(theLHS != 0 && theRHS != 0);

	if (theLength == 0)
	{
		return true;
	}
	else
	{
		const Type* const	theEnd = theLHS + theLength;

		while(theTransformFunction(*theLHS) == theTransformFunction(*theRHS))
		{
			++theLHS;

			if (theLHS == theEnd)
			{
				return true;
			}
			else
			{
				++theRHS;
			}
		}

		return false;
	}
}


				   
template <class Type, class SizeType, class FunctionType>
bool
doEqualsIgnoreCase(
			const Type*		theLHS,
			const Type*		theRHS,
			SizeType		theLength,
			FunctionType	theToUpperFunction)
{
	// Check each character, converting to uppercase
	// for the test.
	for(SizeType i = 0; i < theLength; i++)
	{
		const Type	charLHS = theLHS[i];
		const Type	charRHS = theRHS[i];

		if (charLHS != charRHS &&
			Type(theToUpperFunction(charLHS)) != charRHS &&
			charLHS != Type(theToUpperFunction(charRHS)))
		{
			return false;
		}
	}

	return true;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(bool)
equals(
			const XalanDOMChar*			theLHS,
			const XalanDOMChar*			theRHS,
			XalanDOMString::size_type	theLength)
{
	return doEquals(
				theLHS,
				theRHS,
				theLength,
				makeXalanDOMCharIdentityTransform());
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(bool)
equalsIgnoreCaseASCII(
			const XalanDOMChar*			theLHS,
			const XalanDOMChar*			theRHS,
			XalanDOMString::size_type	theLength)
{
	return doEqualsIgnoreCase(
				theLHS,
				theRHS,
				theLength,
				toUpperASCII);
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMCharVectorType&)
MakeXalanDOMCharVector(
			const char*		data,
            XalanDOMCharVectorType& theResult,
			bool			fTranscode)
{
	assert(data != 0);

	if (fTranscode == true)
	{
		// Create a vector which includes the terminating 0.
		TranscodeFromLocalCodePage(data, theResult, true);
	}
	else
	{
		// Include the terminating null byte...
		const XalanDOMString::size_type		theLength = XalanDOMString::length(data) + 1;

		theResult.reserve(theLength);

#if defined(XALAN_NO_ALGORITHMS_WITH_BUILTINS)
		XalanCopy(
#else
		XALAN_STD_QUALIFIER copy(
#endif
			data,
			data + theLength,
			XALAN_STD_QUALIFIER back_inserter(theResult));
	}

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMCharVectorType&)
MakeXalanDOMCharVector(const XalanDOMChar*	data,
                       XalanDOMCharVectorType& theResult)
{
	assert(data != 0);

	const XalanDOMString::size_type		theLength = length(data);

    XalanDOMCharVectorType tmpVector(data, data + theLength + 1,theResult.getMemoryManager());

    theResult.swap(tmpVector);
	// Create a vector which includes the terminating 0.
	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(void)
CopyWideStringToVector(
			const XalanDOMChar*		theString,
			CharVectorType&			theVector)
{
	const XalanDOMString::size_type		theLength = length(theString);

	if (theLength != 0)
	{
		theVector.reserve(theVector.size() + theLength + 1);

		for(XalanDOMString::size_type i = 0; i < theLength; i++)
		{
			// Assert that the truncation will not affect the resulting character.
			assert(theString[i] == char(theString[i]));

			theVector.push_back(char(theString[i]));
		}

		// Put a terminating 0 byte.
		theVector.push_back(0);
	}
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(void)
CopyStringToVector(
			const char*			theString,
			CharVectorType&		theVector)
{
	theVector.insert(
		theVector.end(),
		theString,
		theString + XalanDOMString::length(theString) + 1);
}



template <class Type>
Type
WideStringToIntegral(
			const XalanDOMChar*		theString,
			Type					/* theDummy */)
{
	if (theString == 0 || DoubleSupport::isValid(theString) == false)
	{
		return Type(0);
	}
	else
	{
		// OK, now we know we have a valid string, so start converting...
		Type	theResult = 0;

		// Consume any leading whitespace (which we allow)
		while(isXMLWhitespace(*theString) == true)
		{
			++theString;
		}

		const bool	isNegative = *theString == XalanUnicode::charHyphenMinus ? true : false;

		if (isNegative == true)
		{
			++theString;
		}

		while(*theString != 0)
		{
			if (*theString >= XalanUnicode::charDigit_0 && *theString <= XalanUnicode::charDigit_9)
			{
				theResult *= 10;

				theResult += *theString - XalanUnicode::charDigit_0;

				++theString;
			}
			else if (isXMLWhitespace(*theString) == true)
			{
				// This must be trailing whitespace...
				break;
			}
			else
			{
				// An non-numeric character was encountered, so stop...
				return 0;
			}
		}

		return isNegative == true ? -theResult : theResult;
	}
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(int)
WideStringToInt(const XalanDOMChar*		theString)
{
	return WideStringToIntegral(theString, int(0));
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(long)
WideStringToLong(const XalanDOMChar*	theString)
{
	return WideStringToIntegral(theString, long(0));
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(unsigned long)
WideStringToUnsignedLong(const XalanDOMChar*	theString)
{
	return WideStringToIntegral(theString, (unsigned long)0);
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(double)
WideStringToDouble(const XalanDOMChar*	theString,
                   MemoryManagerType&   theManager)
{
	return DoubleSupport::toDouble(theString, theManager);
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
trim(const XalanDOMString&	theString,
     XalanDOMString& theResult)
{
	if (isEmpty(theString))
    {
        theResult.erase();
		return theResult;
    }

	const XalanDOMString::size_type		strLen = length(theString);
	assert(strLen > 0);

	// index of first non-whitespace character
	XalanDOMString::size_type	leadingSpace = 0;

	for (; leadingSpace < strLen; ++leadingSpace)
		if (!isXMLWhitespace(charAt(theString, leadingSpace)))
			break;

	// index of last non-whitespace character
	XalanDOMString::size_type	trailingSpace = strLen -1;

	for (; trailingSpace > 0; --trailingSpace)
		if (!isXMLWhitespace(charAt(theString, trailingSpace)))
			break;

    substring(theString, theResult, leadingSpace, trailingSpace +1);

	return theResult;
}



// A very cheap decimal number transcoder...
template <class InputCharType, class OutputCharType>
class DecimalNumberTranscodeTransform
{
public:

	OutputCharType
	operator()(InputCharType	theChar) const
	{
		switch(theChar)
		{
		case '-':
			return OutputCharType(XalanUnicode::charHyphenMinus);
			break;

		case '.':
			return OutputCharType(XalanUnicode::charFullStop);
			break;

		case '0':
			return OutputCharType(XalanUnicode::charDigit_0);
			break;

		case '1':
			return OutputCharType(XalanUnicode::charDigit_1);
			break;

		case '2':
			return OutputCharType(XalanUnicode::charDigit_2);
			break;

		case '3':
			return OutputCharType(XalanUnicode::charDigit_3);
			break;

		case '4':
			return OutputCharType(XalanUnicode::charDigit_4);
			break;

		case '5':
			return OutputCharType(XalanUnicode::charDigit_5);
			break;

		case '6':
			return OutputCharType(XalanUnicode::charDigit_6);
			break;

		case '7':
			return OutputCharType(XalanUnicode::charDigit_7);
			break;

		case '8':
			return OutputCharType(XalanUnicode::charDigit_8);
			break;

		case '9':
			return OutputCharType(XalanUnicode::charDigit_9);
			break;

		default:
			return OutputCharType(0);
			break;
		}
	}
};



template<class InputCharType, class OutputCharType>
DecimalNumberTranscodeTransform<InputCharType, OutputCharType>
makeDecimalNumberTranscodeTransform(
			const InputCharType*,
			const OutputCharType*)
{
	return DecimalNumberTranscodeTransform<InputCharType, OutputCharType>();
}



// A very cheap hex number transcoder...
template <class InputCharType, class OutputCharType>
class HexadecimalNumberTranscodeTransform : public DecimalNumberTranscodeTransform<InputCharType, OutputCharType>
{
public:

	typedef DecimalNumberTranscodeTransform<InputCharType, OutputCharType>	BaseClassType;

	OutputCharType
	operator()(InputCharType	theChar) const
	{
		switch(theChar)
		{
		case 'A':
			return OutputCharType(XalanUnicode::charLetter_A);
			break;

		case 'a':
			return OutputCharType(XalanUnicode::charLetter_a);
			break;

		case 'B':
			return OutputCharType(XalanUnicode::charLetter_B);
			break;

		case 'b':
			return OutputCharType(XalanUnicode::charLetter_b);
			break;

		case 'C':
			return OutputCharType(XalanUnicode::charLetter_C);
			break;

		case 'c':
			return OutputCharType(XalanUnicode::charLetter_c);
			break;

		case 'D':
			return OutputCharType(XalanUnicode::charLetter_D);
			break;

		case 'd':
			return OutputCharType(XalanUnicode::charLetter_d);
			break;

		case 'E':
			return OutputCharType(XalanUnicode::charLetter_E);
			break;

		case 'e':
			return OutputCharType(XalanUnicode::charLetter_e);
			break;

		case 'F':
			return OutputCharType(XalanUnicode::charLetter_F);
			break;

		case 'f':
			return OutputCharType(XalanUnicode::charLetter_f);
			break;

		default:
			return BaseClassType::operator()(theChar);
			break;
		}
	}
};



template <class InputIteratorType, class OutputIteratorType>
OutputIteratorType
TranscodeNumber(
			InputIteratorType	theInputBegin,
			InputIteratorType	theInputEnd,
			OutputIteratorType	theOutputIterator)
{
	return TransformString(
				theInputBegin,
				theInputEnd,
				theOutputIterator,
#if defined(XALAN_NON_ASCII_PLATFORM)
				HexadecimalNumberTranscodeTransform<char, XalanDOMChar>());
#else
				IdentityTransform<char, XalanDOMChar>());
#endif
}



static const char* const	thePrintfStrings[] =
{
	"%.10f",
	"%.11f",
	"%.12f",
	"%.13f",
	"%.14f",
	"%.15f",
	"%.16f",
	"%.17f",
	"%.18f",
	"%.19f",
	"%.20f",
	"%.21f",
	"%.22f",
	"%.23f",
	"%.24f",
	"%.25f",
	"%.26f",
	"%.27f",
	"%.28f",
	"%.29f",
	"%.30f",
	"%.31f",
	"%.32f",
	"%.33f",
	"%.34f",
	"%.35f",
	0
};




XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
PointerToDOMString(
			const void*			theValue,
			XalanDOMString&		theResult)
{
	char			theBuffer[MAX_PRINTF_DIGITS + 1];

#if defined(XALAN_STRICT_ANSI_HEADERS)
	XALAN_USING_STD(sprintf);
#endif

	unsigned int	theCharsWritten = sprintf(theBuffer, "%p", theValue);
	assert(theCharsWritten != 0);

	reserve(theResult, length(theResult) + theCharsWritten);

	TranscodeNumber(
			theBuffer,
			theBuffer + theCharsWritten,
			XALAN_STD_QUALIFIER back_inserter(theResult));

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
DoubleToDOMString(
			double				theDouble,
			XalanDOMString&		theResult)
{
	if (DoubleSupport::isNaN(theDouble) == true)
	{
		append(
			theResult,
			theNaNString,
			sizeof(theNaNString) / sizeof(theNaNString[0]) - 1);
	}
	else if (DoubleSupport::isPositiveInfinity(theDouble) == true)
	{
		append(
			theResult,
			thePositiveInfinityString,
			sizeof(thePositiveInfinityString) / sizeof(thePositiveInfinityString[0]) - 1);
	}
	else if (DoubleSupport::isNegativeInfinity(theDouble) == true)
	{
		append(
			theResult,
			theNegativeInfinityString,
			sizeof(theNegativeInfinityString) / sizeof(theNegativeInfinityString[0]) - 1);
	}
	else if (DoubleSupport::isPositiveZero(theDouble) == true ||
			 DoubleSupport::isNegativeZero(theDouble) == true)
	{
		append(
			theResult,
			theZeroString,
			sizeof(theZeroString) / sizeof(theZeroString[0]) - 1);
	}
	else if (long(theDouble) == theDouble)
	{
		LongToDOMString(long(theDouble), theResult);
	}
	else
	{
		char			theBuffer[MAX_PRINTF_DIGITS + 1];

#if defined(XALAN_STRICT_ANSI_HEADERS)
		XALAN_USING_STD(sprintf)
		XALAN_USING_STD(atof)
		XALAN_USING_STD(isdigit)
#endif

		const char* const *		thePrintfString = thePrintfStrings;

		int		theCharsWritten = 0;

		do
		{
			theCharsWritten = sprintf(theBuffer, *thePrintfString, theDouble);
			assert(theCharsWritten != 0);

			++thePrintfString;
		}
		while(atof(theBuffer) != theDouble && *thePrintfString != 0);

		// First, cleanup the output to conform to the XPath standard,
		// which says no trailing '0's for the decimal portion.
		// So start with the last digit, and search until we find
		// the last correct character for the output.
		// Also, according to the XPath standard, any values without
		// a fractional part are printed as integers.  There's always
		// a decimal point, so we have to strip stuff away...

		// Now, move back while there are zeros...
		while(theBuffer[--theCharsWritten] == '0')
		{
		}

		int		theCurrentIndex = theCharsWritten;

		// If a decimal point stopped the loop, then
		// we don't want to preserve it.  Otherwise,
		// another digit stopped the loop, so we must
		// preserve it.
		if(isdigit(theBuffer[theCharsWritten]))
		{
			++theCharsWritten;
		}

		// Some other character other than '.' can be the
		// separator.  This can happen if the locale is
		// not the "C" locale, etc.  If that's the case,
		// replace it with '.'.
		while(theCurrentIndex > 0)
		{
			if (isdigit(theBuffer[theCurrentIndex]))
			{
				--theCurrentIndex;
			}
			else
			{
				if (theBuffer[theCurrentIndex] != '.')
				{
					theBuffer[theCurrentIndex] = '.';
				}

				break;
			}
		}

		reserve(theResult, length(theResult) + theCharsWritten);

		TranscodeNumber(
				theBuffer,
				theBuffer + theCharsWritten,
				XALAN_STD_QUALIFIER back_inserter(theResult));
	}

	return theResult;
}



void
DOMStringHelper::DoubleToCharacters(
			double				theDouble,
			FormatterListener&	formatterListener,
			MemberFunctionPtr	function)
{
	if (DoubleSupport::isNaN(theDouble) == true)
	{
		(formatterListener.*function)(
			theNaNString,
			sizeof(theNaNString) / sizeof(theNaNString[0]) - 1);
	}
	else if (DoubleSupport::isPositiveInfinity(theDouble) == true)
	{
		(formatterListener.*function)(
			thePositiveInfinityString,
			sizeof(thePositiveInfinityString) / sizeof(thePositiveInfinityString[0]) - 1);
	}
	else if (DoubleSupport::isNegativeInfinity(theDouble) == true)
	{
		(formatterListener.*function)(
			theNegativeInfinityString,
			sizeof(theNegativeInfinityString) / sizeof(theNegativeInfinityString[0]) - 1);
	}
	else if (DoubleSupport::isPositiveZero(theDouble) == true ||
			 DoubleSupport::isNegativeZero(theDouble) == true)
	{
		(formatterListener.*function)(
			theZeroString,
			sizeof(theZeroString) / sizeof(theZeroString[0]) - 1);
	}
	else if (long(theDouble) == theDouble)
	{
		LongToCharacters(long(theDouble), formatterListener, function);
	}
	else
	{
		char			theBuffer[MAX_PRINTF_DIGITS + 1];

#if defined(XALAN_STRICT_ANSI_HEADERS)
		XALAN_USING_STD(sprintf)
		XALAN_USING_STD(atof)
		XALAN_USING_STD(isdigit)
#endif

		const char* const *		thePrintfString = thePrintfStrings;

		int		theCharsWritten = 0;

		do
		{
			theCharsWritten = sprintf(theBuffer, *thePrintfString, theDouble);
			assert(theCharsWritten != 0);

			++thePrintfString;
		}
		while(atof(theBuffer) != theDouble && *thePrintfString != 0);

		// First, cleanup the output to conform to the XPath standard,
		// which says no trailing '0's for the decimal portion.
		// So start with the last digit, and search until we find
		// the last correct character for the output.
		// Also, according to the XPath standard, any values without
		// a fractional part are printed as integers.  There's always
		// a decimal point, so we have to strip stuff away...

		// Now, move back while there are zeros...
		while(theBuffer[--theCharsWritten] == '0')
		{
		}

		int		theCurrentIndex = theCharsWritten;

		// If a decimal point stopped the loop, then
		// we don't want to preserve it.  Otherwise,
		// another digit stopped the loop, so we must
		// preserve it.
		if(isdigit(theBuffer[theCharsWritten]))
		{
			++theCharsWritten;
		}

		// Some other character other than '.' can be the
		// separator.  This can happen if the locale is
		// not the "C" locale, etc.  If that's the case,
		// replace it with '.'.
		while(theCurrentIndex > 0)
		{
			if (isdigit(theBuffer[theCurrentIndex]))
			{
				--theCurrentIndex;
			}
			else
			{
				if (theBuffer[theCurrentIndex] != '.')
				{
					theBuffer[theCurrentIndex] = '.';
				}

				break;
			}
		}

		XalanDOMChar	theResult[MAX_PRINTF_DIGITS + 1];

		TranscodeNumber(
				theBuffer,
				theBuffer + theCharsWritten,
				&theResult[0]);

		(formatterListener.*function)(
			theResult,
			theCharsWritten);
	}
}



template <class ScalarType>
XalanDOMChar*
ScalarToDecimalString(
			ScalarType		theValue,
			XalanDOMChar*	theOutput)
{
	// Null terminate it...
	*theOutput = 0;

	if (theValue < 0)
	{
		do
		{
			*--theOutput = XalanDOMChar(-(theValue % 10) + XalanUnicode::charDigit_0);

			// OK, we're done with it...
			theValue /= 10;
		}
		while(theValue != 0);

		*--theOutput = XalanUnicode::charHyphenMinus;
	}
	else
	{
		do
		{
			*--theOutput = XalanDOMChar(theValue % 10 + XalanUnicode::charDigit_0);

			// OK, we're done with it...
			theValue /= 10;
		}
		while(theValue != 0);
	}

	return theOutput;
}



template <class ScalarType>
void
ScalarToDecimalString(
			ScalarType			theValue,
			XalanDOMString&		theResult)
{
	// We don't need to transcode, so just make it a
	// wide character string...
	XalanDOMChar			theBuffer[MAX_PRINTF_DIGITS + 1];

	XalanDOMChar* const		theEnd = &theBuffer[MAX_PRINTF_DIGITS];

	XalanDOMChar* const		theBegin = ScalarToDecimalString(theValue, theEnd);

	append(
        theResult,
        theBegin,
        XalanDOMString::size_type(theEnd - theBegin));
}



template <class ScalarType>
XalanDOMChar*
UnsignedScalarToHexadecimalString(
			ScalarType		theValue,
			XalanDOMChar*	theOutput)
{
	if (theValue >= 0)
	{
		// Null terminate it...
		*theOutput = 0;

		do
		{
			// Next spot...
			--theOutput;

			const ScalarType	theTemp = theValue % 16;

			// Isolate the left most character.
			if (theTemp >= 0 && theTemp <= 9)
			{
				*theOutput = XalanDOMChar(theTemp + XalanUnicode::charDigit_0);
			}
			else
			{
				assert(theTemp >= 10 && theTemp <= 15);

				*theOutput = XalanDOMChar(theTemp - 10 + XalanUnicode::charLetter_A);
			}

			// OK, we're done with it...
			theValue /= 16;
		}
		while(theValue != 0);
	}

	return theOutput;
}



template <class ScalarType>
void
UnsignedScalarToHexadecimalString(
			ScalarType			theValue,
			XalanDOMString&		theResult)
{
	if (theValue >= 0)
	{
		// We don't need to transcode, so just make it a
		// wide character string...
		XalanDOMChar			theBuffer[MAX_PRINTF_DIGITS + 1];

		XalanDOMChar* const		theEnd = &theBuffer[MAX_PRINTF_DIGITS];

		XalanDOMChar* const		theBegin = UnsignedScalarToHexadecimalString(theValue, theEnd);

		append(
            theResult,
            theBegin,
            XalanDOMString::size_type(theEnd - theBegin));
	}
}



void
DOMStringHelper::LongToCharacters(
			long				theLong,
			FormatterListener&	formatterListener,
			MemberFunctionPtr	function)
{
	XalanDOMChar	theBuffer[MAX_PRINTF_DIGITS + 1];

	const XalanDOMChar* const	theResult = ScalarToDecimalString(theLong, &theBuffer[MAX_PRINTF_DIGITS]);

	(formatterListener.*function)(theResult, XalanDOMString::length(theResult));
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
LongToHexDOMString(
			long				theValue,
			XalanDOMString&		theResult)
{
	UnsignedScalarToHexadecimalString(theValue, theResult);

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
UnsignedLongToHexDOMString(
			unsigned long		theValue,
			XalanDOMString&		theResult)
{
	UnsignedScalarToHexadecimalString(theValue, theResult);

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
LongToDOMString(
			long				theValue,
			XalanDOMString&		theResult)
{
	ScalarToDecimalString(theValue, theResult);

	return theResult;
}


XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(XalanDOMString&)
UnsignedLongToDOMString(
			unsigned long		theValue,
			XalanDOMString&		theResult)
{
	ScalarToDecimalString(theValue, theResult);

	return theResult;
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(bool)
isXMLWhitespace(const XalanDOMString&	string)
{
	const XalanDOMString::size_type		theLength = length(string);

	if (theLength == 0)
	{
		return true;
	}
	else
	{
		const XalanDOMChar* const	theBuffer =
			c_wstr(string);

		assert(theBuffer != 0);

		return isXMLWhitespace(theBuffer, 0, theLength);
	}
}



XALAN_PLATFORMSUPPORT_EXPORT_FUNCTION(bool)
isXMLWhitespace(
			const XalanDOMChar			ch[],
			XalanDOMString::size_type	start,
			XalanDOMString::size_type	length)
{
	const XalanDOMString::size_type		end = start + length;

	for(XalanDOMString::size_type s = start; s < end; s++) 
	{
		if (!isXMLWhitespace(ch[s]))	
			return false;
	}

	return true;
}


XALAN_CPP_NAMESPACE_END
