/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 * 
 *************************************************************/




#include <com/sun/star/i18n/UnicodeType.hpp>
#include <com/sun/star/util/DateTime.hpp>
#include <com/sun/star/util/Date.hpp>
#include <com/sun/star/util/Duration.hpp>
#include <com/sun/star/uno/Sequence.hxx>

#include <rtl/ustrbuf.hxx>
#include <rtl/math.hxx>
#include "sax/tools/converter.hxx"

using namespace rtl;
using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::util;
//using namespace com::sun::star::text;
//using namespace com::sun::star::style;
using namespace ::com::sun::star::i18n;

namespace sax {

static const sal_Char* gpsMM = "mm";
static const sal_Char* gpsCM = "cm";
static const sal_Char* gpsPT = "pt";
static const sal_Char* gpsINCH = "in";
static const sal_Char* gpsPC = "pc";

const sal_Int8 XML_MAXDIGITSCOUNT_TIME = 11;
const sal_Int8 XML_MAXDIGITSCOUNT_DATETIME = 6;
#define XML_NULLDATE "NullDate"

/** convert string to measure using optional min and max values*/
bool Converter::convertMeasure(	sal_Int32& rValue,
								const OUString& rString,
								sal_Int16 nTargetUnit /* = MeasureUnit::MM_100TH */,
								sal_Int32 nMin /* = SAL_MIN_INT32 */,
								sal_Int32 nMax /* = SAL_MAX_INT32 */ )
{
    bool bNeg = false;
    double nVal = 0;

    sal_Int32 nPos = 0;
    sal_Int32 nLen = rString.getLength();

    // skip white space
    while( (nPos < nLen) && (rString[nPos] <= sal_Unicode(' ')) )
        nPos++;

    if( nPos < nLen && sal_Unicode('-') == rString[nPos] )
    {
        bNeg = true;
        nPos++;
    }

    // get number
    while( nPos < nLen &&
           sal_Unicode('0') <= rString[nPos] &&
           sal_Unicode('9') >= rString[nPos] )
    {
        // TODO: check overflow!
        nVal *= 10;
        nVal += (rString[nPos] - sal_Unicode('0'));
        nPos++;
    }
    double nDiv = 1.;
    if( nPos < nLen && sal_Unicode('.') == rString[nPos] )
    {
        nPos++;

        while( nPos < nLen &&
               sal_Unicode('0') <= rString[nPos] &&
               sal_Unicode('9') >= rString[nPos] )
        {
            // TODO: check overflow!
            nDiv *= 10;
            nVal += ( ((double)(rString[nPos] - sal_Unicode('0'))) / nDiv );
            nPos++;
        }
    }

    // skip white space
    while( (nPos < nLen) && (rString[nPos] <= sal_Unicode(' ')) )
        nPos++;

    if( nPos < nLen )
    {

		if( MeasureUnit::PERCENT == nTargetUnit )
        {
            if( sal_Unicode('%') != rString[nPos] )
                return false;
        }
		else if( MeasureUnit::PIXEL == nTargetUnit )
        {
            if( nPos + 1 >= nLen ||
                (sal_Unicode('p') != rString[nPos] &&
                 sal_Unicode('P') != rString[nPos])||
                (sal_Unicode('x') != rString[nPos+1] &&
                 sal_Unicode('X') != rString[nPos+1]) )
                return false;
        }
        else
        {
            OSL_ENSURE( MeasureUnit::TWIP == nTargetUnit || MeasureUnit::POINT == nTargetUnit ||
                        MeasureUnit::MM_100TH == nTargetUnit || MeasureUnit::MM_10TH == nTargetUnit, "unit is not supported");
            const sal_Char *aCmpsL[2] = { 0, 0 };
            const sal_Char *aCmpsU[2] = { 0, 0 };
            double aScales[2] = { 1., 1. };

            if( MeasureUnit::TWIP == nTargetUnit )
            {
                switch( rString[nPos] )
                {
                case sal_Unicode('c'):
                case sal_Unicode('C'):
                    aCmpsL[0] = "cm";
                    aCmpsU[0] = "CM";
                    aScales[0] = (72.*20.)/2.54; // twip
                    break;
                case sal_Unicode('i'):
                case sal_Unicode('I'):
                    aCmpsL[0] = "in";
                    aCmpsU[0] = "IN";
                    aScales[0] = 72.*20.; // twip
                    break;
                case sal_Unicode('m'):
                case sal_Unicode('M'):
                    aCmpsL[0] = "mm";
                    aCmpsU[0] = "MM";
                    aScales[0] = (72.*20.)/25.4; // twip
                    break;
                case sal_Unicode('p'):
                case sal_Unicode('P'):
                    aCmpsL[0] = "pt";
                    aCmpsU[0] = "PT";
                    aScales[0] = 20.; // twip

                    aCmpsL[1] = "pc";
                    aCmpsU[1] = "PC";
                    aScales[1] = 12.*20.; // twip
                    break;
                }
            }
            else if( MeasureUnit::MM_100TH == nTargetUnit || MeasureUnit::MM_10TH == nTargetUnit )
            {
				double nScaleFactor = (MeasureUnit::MM_100TH == nTargetUnit) ? 100.0 : 10.0;
                switch( rString[nPos] )
                {
                case sal_Unicode('c'):
                case sal_Unicode('C'):
                    aCmpsL[0] = "cm";
                    aCmpsU[0] = "CM";
                    aScales[0] = 10.0 * nScaleFactor; // mm/100
                    break;
                case sal_Unicode('i'):
                case sal_Unicode('I'):
                    aCmpsL[0] = "in";
                    aCmpsU[0] = "IN";
                    aScales[0] = 1000.*2.54; // mm/100
                    break;
                case sal_Unicode('m'):
                case sal_Unicode('M'):
                    aCmpsL[0] = "mm";
                    aCmpsU[0] = "MM";
                    aScales[0] = 1.0 * nScaleFactor; // mm/100
                    break;
                case sal_Unicode('p'):
                case sal_Unicode('P'):
                    aCmpsL[0] = "pt";
                    aCmpsU[0] = "PT";
                    aScales[0] = (10.0 * nScaleFactor*2.54)/72.; // mm/100

                    aCmpsL[1] = "pc";
                    aCmpsU[1] = "PC";
                    aScales[1] = (10.0 * nScaleFactor*2.54)/12.; // mm/100
                    break;
                }
            }
            else if( MeasureUnit::POINT == nTargetUnit )
            {
                if( rString[nPos] == 'p' || rString[nPos] == 'P' )
                {
                    aCmpsL[0] = "pt";
                    aCmpsU[0] = "PT";
                    aScales[0] = 1;
                }
            }

            if( aCmpsL[0] == NULL )
                return false;

            double nScale = 0.;
            for( sal_uInt16 i= 0; i < 2; i++ )
            {
                const sal_Char *pL = aCmpsL[i];
                if( pL )
                {
                    const sal_Char *pU = aCmpsU[i];
                    while( nPos < nLen && *pL )
                    {
                        sal_Unicode c = rString[nPos];
                        if( c != *pL && c != *pU )
                            break;
                        pL++;
                        pU++;
                        nPos++;
                    }
                    if( !*pL && (nPos == nLen || ' ' == rString[nPos]) )
                    {
                        nScale = aScales[i];
                        break;
                    }
                }
            }

            if( 0. == nScale )
                return false;

            // TODO: check overflow
            if( nScale != 1. )
                nVal *= nScale;
        }
    }

    nVal += .5;
    if( bNeg )
        nVal = -nVal;

    if( nVal <= (double)nMin )
        rValue = nMin;
    else if( nVal >= (double)nMax )
        rValue = nMax;
    else
        rValue = (sal_Int32)nVal;

    return true;
}

/** convert measure in given unit to string with given unit */
void Converter::convertMeasure( OUStringBuffer& rBuffer,
								sal_Int32 nMeasure,
								sal_Int16 nSourceUnit /* = MeasureUnit::MM_100TH */,
								sal_Int16 nTargetUnit /* = MeasureUnit::INCH */  )
{
    OSL_ENSURE( false, "Converter::convertMeasure - not implemented, tools/BigInt needs replacement" );
    (void)rBuffer;
    (void)nMeasure;
    (void)nSourceUnit;
    (void)nTargetUnit;
#if 0
    if( nSourceUnit == MeasureUnit::PERCENT )
    {
        OSL_ENSURE( nTargetUnit == MeasureUnit::PERCENT,
                    "MeasureUnit::PERCENT only maps to MeasureUnit::PERCENT!" );

        rBuffer.append( nMeasure );
        rBuffer.append( sal_Unicode('%' ) );
    }
    else
    {
	// the sign is processed seperatly
	if( nMeasure < 0 )
	{
		nMeasure = -nMeasure;
		rBuffer.append( sal_Unicode('-') );
	}

	// The new length is (nVal * nMul)/(nDiv*nFac*10)
	long nMul = 1000;
	long nDiv = 1;
	long nFac = 100;
	const sal_Char* psUnit = 0;
	switch( nSourceUnit )
	{
	case MeasureUnit::TWIP:
		switch( nTargetUnit )
		{
		case MeasureUnit::MM_100TH:
		case MeasureUnit::MM_10TH:
            OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,"output unit not supported for twip values" );
		case MeasureUnit::MM:
			// 0.01mm = 0.57twip (exactly)
			nMul = 25400;	// 25.4 * 1000
			nDiv = 1440;	// 72 * 20;
			nFac = 100;
			psUnit = gpsMM;
			break;

		case MeasureUnit::CM:
			// 0.001cm = 0.57twip (exactly)
			nMul = 25400;	// 2.54 * 10000
			nDiv = 1440;	// 72 * 20;
			nFac = 1000;
			psUnit = gpsCM;
			break;

		case MeasureUnit::POINT:
			// 0.01pt = 0.2twip (exactly)
			nMul = 1000;
			nDiv = 20;
			nFac = 100;
			psUnit = gpsPT;
			break;

		case MeasureUnit::INCH:
		default:
            OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,
						"output unit not supported for twip values" );
			// 0.0001in = 0.144twip (exactly)
			nMul = 100000;
			nDiv = 1440;	// 72 * 20;
			nFac = 10000;
			psUnit = gpsINCH;
			break;
		}
		break;

	case MeasureUnit::POINT:
		// 1pt = 1pt (exactly)
        OSL_ENSURE( MeasureUnit::POINT == nTargetUnit,
					"output unit not supported for pt values" );
		nMul = 10;
		nDiv = 1;
		nFac = 1;
		psUnit = gpsPT;
		break;
    case MeasureUnit::MM_10TH:
	case MeasureUnit::MM_100TH:
        {
            long nFac2 = (MeasureUnit::MM_100TH == nSourceUnit) ? 100 : 10;
		    switch( nTargetUnit )
		    {
		    case MeasureUnit::MM_100TH:
		    case MeasureUnit::MM_10TH:
                OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,
						    "output unit not supported for 1/100mm values" );
		    case MeasureUnit::MM:
			    // 0.01mm = 1 mm/100 (exactly)
			    nMul = 10;
			    nDiv = 1;
			    nFac = nFac2;
			    psUnit = gpsMM;
			    break;

		    case MeasureUnit::CM:
			    // 0.001mm = 1 mm/100 (exactly)
			    nMul = 10;
			    nDiv = 1;	// 72 * 20;
			    nFac = 10*nFac2;
			    psUnit = gpsCM;
			    break;

		    case MeasureUnit::POINT:
			    // 0.01pt = 0.35 mm/100 (exactly)
			    nMul = 72000;
			    nDiv = 2540;
			    nFac = nFac2;
			    psUnit = gpsPT;
			    break;

		    case MeasureUnit::INCH:
		    default:
                OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,
						    "output unit not supported for 1/100mm values" );
			    // 0.0001in = 0.254 mm/100 (exactly)
			    nMul = 100000;
			    nDiv = 2540;
			    nFac = 100*nFac2;
			    psUnit = gpsINCH;
			    break;
		    }
		    break;
        }
	}

	long nLongVal = 0;
	bool bOutLongVal = true;
	if( nMeasure > SAL_INT32_MAX / nMul )
	{
		// A big int is required for calculation
		BigInt nBigVal( nMeasure );
		BigInt nBigFac( nFac );
		nBigVal *= nMul;
		nBigVal /= nDiv;
		nBigVal += 5;
		nBigVal /= 10;

		if( nBigVal.IsLong() )
		{
			// To convert the value into a string a long is sufficient
			nLongVal = (long)nBigVal;
		}
		else
		{
			BigInt nBigFac2( nFac );
			BigInt nBig10( 10 );
			rBuffer.append( (sal_Int32)(nBigVal / nBigFac2) );
			if( !(nBigVal % nBigFac2).IsZero() )
			{
				rBuffer.append( sal_Unicode('.') );
				while( nFac > 1 && !(nBigVal % nBigFac2).IsZero() )
				{
					nFac /= 10;
					nBigFac2 = nFac;
					rBuffer.append( (sal_Int32)((nBigVal / nBigFac2) % nBig10 ) );
				}
			}
			bOutLongVal = false;
		}
	}
	else
	{
		nLongVal = nMeasure * nMul;
		nLongVal /= nDiv;
		nLongVal += 5;
		nLongVal /= 10;
	}

	if( bOutLongVal )
	{
		rBuffer.append( (sal_Int32)(nLongVal / nFac) );
		if( nFac > 1 && (nLongVal % nFac) != 0 )
		{
			rBuffer.append( sal_Unicode('.') );
			while( nFac > 1 && (nLongVal % nFac) != 0 )
			{
				nFac /= 10;
				rBuffer.append( (sal_Int32)((nLongVal / nFac) % 10) );
			}
		}
	}

	if( psUnit )
		rBuffer.appendAscii( psUnit );
    }
#endif
}

static const OUString& getTrueString()
{
	static const OUString sTrue( RTL_CONSTASCII_USTRINGPARAM( "true" ) );
	return sTrue;
}

static const OUString& getFalseString()
{
	static const OUString sFalse( RTL_CONSTASCII_USTRINGPARAM( "false" ) );
	return sFalse;
}

/** convert string to boolean */
bool Converter::convertBool( bool& rBool, const OUString& rString )
{
    rBool = rString == getTrueString();

    return rBool || (rString == getFalseString());
}

/** convert boolean to string */
void Converter::convertBool( OUStringBuffer& rBuffer, bool bValue )
{
    rBuffer.append( bValue ? getTrueString() : getFalseString() );
}

/** convert string to percent */
bool Converter::convertPercent( sal_Int32& rPercent, const OUString& rString )
{
    return convertMeasure( rPercent, rString, MeasureUnit::PERCENT );
}

/** convert percent to string */
void Converter::convertPercent( OUStringBuffer& rBuffer, sal_Int32 nValue )
{
    rBuffer.append( nValue );
    rBuffer.append( sal_Unicode('%' ) );
}

/** convert string to pixel measure */
bool Converter::convertMeasurePx( sal_Int32& rPixel, const OUString& rString )
{
    return convertMeasure( rPixel, rString, MeasureUnit::PIXEL );
}

/** convert pixel measure to string */
void Converter::convertMeasurePx( OUStringBuffer& rBuffer, sal_Int32 nValue )
{
    rBuffer.append( nValue );
    rBuffer.append( sal_Unicode('p' ) );
    rBuffer.append( sal_Unicode('x' ) );
}

int lcl_gethex( int nChar )
{
    if( nChar >= '0' && nChar <= '9' )
        return nChar - '0';
    else if( nChar >= 'a' && nChar <= 'f' )
        return nChar - 'a' + 10;
    else if( nChar >= 'A' && nChar <= 'F' )
        return nChar - 'A' + 10;
    else
        return 0;
}

/** convert string to color */
bool Converter::convertColor( sal_Int32& rColor, const OUString& rValue )
{
    if( rValue.getLength() != 7 || rValue[0] != '#' )
        return false;

	rColor = lcl_gethex( rValue[1] ) * 16 + lcl_gethex( rValue[2] );
	rColor <<= 8;

	rColor |= ( lcl_gethex( rValue[3] ) * 16 + lcl_gethex( rValue[4] ) );
	rColor <<= 8;

	rColor |= ( lcl_gethex( rValue[5] ) * 16 + lcl_gethex( rValue[6] ) );

    return true;
}

static sal_Char aHexTab[] = "0123456789abcdef";

/** convert color to string */
void Converter::convertColor( OUStringBuffer& rBuffer, sal_Int32 nColor )
{
    rBuffer.append( sal_Unicode( '#' ) );

    sal_uInt8 nCol = (sal_uInt8)(nColor >> 16);
    rBuffer.append( sal_Unicode( aHexTab[ nCol >> 4 ] ) );
    rBuffer.append( sal_Unicode( aHexTab[ nCol & 0xf ] ) );

    nCol = (sal_uInt8)(nColor >> 8);
    rBuffer.append( sal_Unicode( aHexTab[ nCol >> 4 ] ) );
    rBuffer.append( sal_Unicode( aHexTab[ nCol & 0xf ] ) );

    nCol = (sal_uInt8)nColor;
    rBuffer.append( sal_Unicode( aHexTab[ nCol >> 4 ] ) );
    rBuffer.append( sal_Unicode( aHexTab[ nCol & 0xf ] ) );
}

/** convert number to string */
void Converter::convertNumber( OUStringBuffer& rBuffer, sal_Int32 nNumber )
{
    rBuffer.append( nNumber );
}

/** convert string to number with optional min and max values */
bool Converter::convertNumber(	sal_Int32& rValue,
								const OUString& rString,
								sal_Int32 nMin, sal_Int32 nMax )
{
    bool bNeg = false;
    rValue = 0;

    sal_Int32 nPos = 0;
    sal_Int32 nLen = rString.getLength();

    // skip white space
    while( (nPos < nLen) && (rString[nPos] <= sal_Unicode(' ')) )
        nPos++;

    if( nPos < nLen && sal_Unicode('-') == rString[nPos] )
    {
        bNeg = true;
        nPos++;
    }

    // get number
    while( nPos < nLen &&
           sal_Unicode('0') <= rString[nPos] &&
           sal_Unicode('9') >= rString[nPos] )
    {
        // TODO: check overflow!
        rValue *= 10;
        rValue += (rString[nPos] - sal_Unicode('0'));
        nPos++;
    }

    if( bNeg )
        rValue *= -1;

	if( rValue < nMin )
		rValue = nMin;
	else if( rValue > nMax )
		rValue = nMax;

    return nPos == nLen;
}

/** convert double number to string (using ::rtl::math) */
void Converter::convertDouble(  OUStringBuffer& rBuffer,
								double fNumber,
								bool bWriteUnits,
								sal_Int16 nSourceUnit,
								sal_Int16 nTargetUnit)
{
    if(MeasureUnit::PERCENT == nSourceUnit)
    {
        OSL_ENSURE( nTargetUnit == MeasureUnit::PERCENT, "MeasureUnit::PERCENT only maps to MeasureUnit::PERCENT!" );
        ::rtl::math::doubleToUStringBuffer( rBuffer, fNumber, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
        if(bWriteUnits)
            rBuffer.append(sal_Unicode('%'));
    }
    else
    {
        OUStringBuffer sUnit;
        double fFactor = GetConversionFactor(sUnit, nSourceUnit, nTargetUnit);
        if(fFactor != 1.0)
            fNumber *= fFactor;
        ::rtl::math::doubleToUStringBuffer( rBuffer, fNumber, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
        if(bWriteUnits)
            rBuffer.append(sUnit);
    }
}

/** convert double number to string (using ::rtl::math) */
void Converter::convertDouble( ::rtl::OUStringBuffer& rBuffer, double fNumber)
{
    ::rtl::math::doubleToUStringBuffer( rBuffer, fNumber, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
}

/** convert string to double number (using ::rtl::math) */
bool Converter::convertDouble(double& rValue,
    const ::rtl::OUString& rString, sal_Int16 nTargetUnit)
{
	sal_Int16 nSourceUnit = GetUnitFromString(rString, nTargetUnit);

	return convertDouble(rValue, rString, nSourceUnit, nTargetUnit );
}

/** convert string to double number (using ::rtl::math) */
bool Converter::convertDouble(double& rValue,
    const ::rtl::OUString& rString, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit)
{
    rtl_math_ConversionStatus eStatus;
    rValue = ::rtl::math::stringToDouble( rString, (sal_Unicode)('.'), (sal_Unicode)(','), &eStatus, NULL );

    if(eStatus == rtl_math_ConversionStatus_Ok)
    {
		OUStringBuffer sUnit;
        double fFactor = GetConversionFactor(sUnit, nSourceUnit, nTargetUnit);
        if(fFactor != 1.0 && fFactor != 0.0)
            rValue /= fFactor;
    }

    return ( eStatus == rtl_math_ConversionStatus_Ok );
}

/** convert string to double number (using ::rtl::math) */
bool Converter::convertDouble(double& rValue, const ::rtl::OUString& rString)
{
    rtl_math_ConversionStatus eStatus;
    rValue = ::rtl::math::stringToDouble( rString, (sal_Unicode)('.'), (sal_Unicode)(','), &eStatus, NULL );
    return ( eStatus == rtl_math_ConversionStatus_Ok );
}

/** convert double to ISO "duration" string; negative durations allowed */
void Converter::convertDuration(::rtl::OUStringBuffer& rBuffer,
                                const double fTime)
{
    double fValue = fTime;

    // take care of negative durations as specified in:
    // XML Schema, W3C Working Draft 07 April 2000, section 3.2.6.1
    if (fValue < 0.0)
    {
        rBuffer.append(sal_Unicode('-'));
        fValue = - fValue;
    }

    rBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM( "PT" ));
    fValue *= 24;
    double fHoursValue = ::rtl::math::approxFloor (fValue);
    fValue -= fHoursValue;
    fValue *= 60;
    double fMinsValue = ::rtl::math::approxFloor (fValue);
    fValue -= fMinsValue;
    fValue *= 60;
    double fSecsValue = ::rtl::math::approxFloor (fValue);
    fValue -= fSecsValue;
    double f100SecsValue;
    if (fValue > 0.00001)
        f100SecsValue = ::rtl::math::round( fValue, XML_MAXDIGITSCOUNT_TIME - 5);
    else
        f100SecsValue = 0.0;

    if (f100SecsValue == 1.0)
    {
        f100SecsValue = 0.0;
        fSecsValue += 1.0;
    }
    if (fSecsValue >= 60.0)
    {
        fSecsValue -= 60.0;
        fMinsValue += 1.0;
    }
    if (fMinsValue >= 60.0)
    {
        fMinsValue -= 60.0;
        fHoursValue += 1.0;
    }

    if (fHoursValue < 10)
        rBuffer.append( sal_Unicode('0'));
    rBuffer.append( sal_Int32( fHoursValue));
    rBuffer.append( sal_Unicode('H'));
    if (fMinsValue < 10)
        rBuffer.append( sal_Unicode('0'));
    rBuffer.append( sal_Int32( fMinsValue));
    rBuffer.append( sal_Unicode('M'));
    if (fSecsValue < 10)
        rBuffer.append( sal_Unicode('0'));
    rBuffer.append( sal_Int32( fSecsValue));
    if (f100SecsValue > 0.0)
    {
        ::rtl::OUString a100th( ::rtl::math::doubleToUString( fValue,
                    rtl_math_StringFormat_F, XML_MAXDIGITSCOUNT_TIME - 5, '.',
                    true));
        if ( a100th.getLength() > 2 )
        {
            rBuffer.append( sal_Unicode('.'));
            rBuffer.append( a100th.copy( 2 ) );     // strip 0.
        }
    }
    rBuffer.append( sal_Unicode('S'));
}

/** convert ISO "duration" string to double; negative durations allowed */
bool Converter::convertDuration(double& rfTime,
                                const ::rtl::OUString& rString)
{
    rtl::OUString aTrimmed = rString.trim().toAsciiUpperCase();
    const sal_Unicode* pStr = aTrimmed.getStr();

    // negative time duration?
    bool bIsNegativeDuration = false;
    if ( sal_Unicode('-') == (*pStr) )
    {
        bIsNegativeDuration = true;
        pStr++;
    }

    if ( *(pStr++) != sal_Unicode('P') )            // duration must start with "P"
        return false;

    rtl::OUString sDoubleStr;
    bool bSuccess = true;
    bool bDone = false;
    bool bTimePart = false;
    bool bIsFraction = false;
    sal_Int32 nDays  = 0;
    sal_Int32 nHours = 0;
    sal_Int32 nMins  = 0;
    sal_Int32 nSecs  = 0;
    sal_Int32 nTemp = 0;

    while ( bSuccess && !bDone )
    {
        sal_Unicode c = *(pStr++);
        if ( !c )                               // end
            bDone = true;
        else if ( sal_Unicode('0') <= c && sal_Unicode('9') >= c )
        {
            if ( nTemp >= SAL_MAX_INT32 / 10 )
                bSuccess = false;
            else
            {
                if ( !bIsFraction )
                {
                    nTemp *= 10;
                    nTemp += (c - sal_Unicode('0'));
                }
                else
                {
                    sDoubleStr += OUString::valueOf(c);
                }
            }
        }
        else if ( bTimePart )
        {
            if ( c == sal_Unicode('H') )
            {
                nHours = nTemp;
                nTemp = 0;
            }
            else if ( c == sal_Unicode('M') )
            {
                nMins = nTemp;
                nTemp = 0;
            }
            else if ( (c == sal_Unicode(',')) || (c == sal_Unicode('.')) )
            {
                nSecs = nTemp;
                nTemp = 0;
                bIsFraction = true;
                sDoubleStr = OUString(RTL_CONSTASCII_USTRINGPARAM("0."));
            }
            else if ( c == sal_Unicode('S') )
            {
                if ( !bIsFraction )
                {
                    nSecs = nTemp;
                    nTemp = 0;
                    sDoubleStr = OUString(RTL_CONSTASCII_USTRINGPARAM("0.0"));
                }
            }
            else
                bSuccess = false;               // invalid character
        }
        else
        {
            if ( c == sal_Unicode('T') )            // "T" starts time part
                bTimePart = true;
            else if ( c == sal_Unicode('D') )
            {
                nDays = nTemp;
                nTemp = 0;
            }
            else if ( c == sal_Unicode('Y') || c == sal_Unicode('M') )
            {
                //! how many days is a year or month?

                OSL_ENSURE( false, "years or months in duration: not implemented");
                bSuccess = false;
            }
            else
                bSuccess = false;               // invalid character
        }
    }

    if ( bSuccess )
    {
        if ( nDays )
            nHours += nDays * 24;               // add the days to the hours part
        double fTempTime = 0.0;
        double fHour = nHours;
        double fMin = nMins;
        double fSec = nSecs;
        double fSec100 = 0.0;
        double fFraction = sDoubleStr.toDouble();
        fTempTime = fHour / 24;
        fTempTime += fMin / (24 * 60);
        fTempTime += fSec / (24 * 60 * 60);
        fTempTime += fSec100 / (24 * 60 * 60 * 60);
        fTempTime += fFraction / (24 * 60 * 60);

        // negative duration?
        if ( bIsNegativeDuration )
        {
            fTempTime = -fTempTime;
        }

        rfTime = fTempTime;
    }
    return bSuccess;
}

/** convert util::Duration to ISO "duration" string */
void Converter::convertDuration(::rtl::OUStringBuffer& rBuffer,
        const ::util::Duration& rDuration)
{
    if (rDuration.Negative)
    {
        rBuffer.append(sal_Unicode('-'));
    }
    rBuffer.append(sal_Unicode('P'));
    const bool bHaveDate(static_cast<sal_Int32>(rDuration.Years)
                        +static_cast<sal_Int32>(rDuration.Months)
                        +static_cast<sal_Int32>(rDuration.Days));
    if (rDuration.Years)
    {
        rBuffer.append(static_cast<sal_Int32>(rDuration.Years));
        rBuffer.append(sal_Unicode('Y'));
    }
    if (rDuration.Months)
    {
        rBuffer.append(static_cast<sal_Int32>(rDuration.Months));
        rBuffer.append(sal_Unicode('M'));
    }
    if (rDuration.Days)
    {
        rBuffer.append(static_cast<sal_Int32>(rDuration.Days));
        rBuffer.append(sal_Unicode('D'));
    }
    const sal_Int32 nMSecs(static_cast<sal_Int32>(rDuration.Seconds)
                         + static_cast<sal_Int32>(rDuration.MilliSeconds));
    if (static_cast<sal_Int32>(rDuration.Hours) +
        static_cast<sal_Int32>(rDuration.Minutes) + nMSecs)
    {
        rBuffer.append(sal_Unicode('T')); // time separator
        if (rDuration.Hours)
        {
            rBuffer.append(static_cast<sal_Int32>(rDuration.Hours));
            rBuffer.append(sal_Unicode('H'));
        }
        if (rDuration.Minutes)
        {
            rBuffer.append(static_cast<sal_Int32>(rDuration.Minutes));
            rBuffer.append(sal_Unicode('M'));
        }
        if (nMSecs)
        {
            // seconds must not be omitted (i.e. ".42S" is not valid)
            rBuffer.append(static_cast<sal_Int32>(rDuration.Seconds));
            if (rDuration.MilliSeconds)
            {
                rBuffer.append(sal_Unicode('.'));
                const sal_Int32 nMilliSeconds(rDuration.MilliSeconds % 1000);
                if (nMilliSeconds < 100)
                {
                    rBuffer.append(sal_Unicode('0'));
                }
                if (nMilliSeconds < 10)
                {
                    rBuffer.append(sal_Unicode('0'));
                }
                if (0 == (nMilliSeconds % 10))
                {
                    if (0 == (nMilliSeconds % 100))
                    {
                        rBuffer.append(nMilliSeconds / 100);
                    }
                    else
                    {
                        rBuffer.append(nMilliSeconds / 10);
                    }
                }
                else
                {
                    rBuffer.append(nMilliSeconds);
                }
            }
            rBuffer.append(sal_Unicode('S'));
        }
    }
    else if (!bHaveDate)
    {
        // zero duration: XMLSchema-2 says there must be at least one component
        rBuffer.append(sal_Unicode('0'));
        rBuffer.append(sal_Unicode('D'));
    }
}

enum Result { R_NOTHING, R_OVERFLOW, R_SUCCESS };

static Result
readUnsignedNumber(const ::rtl::OUString & rString,
    sal_Int32 & io_rnPos, sal_Int32 & o_rNumber)
{
    bool bOverflow(false);
    sal_Int32 nTemp(0);
    sal_Int32 nPos(io_rnPos);

    while (nPos < rString.getLength())
    {
        const sal_Unicode c = rString[nPos];
        if ((sal_Unicode('0') <= c) && (c <= sal_Unicode('9')))
        {
            nTemp *= 10;
            nTemp += (c - sal_Unicode('0'));
            if (nTemp >= SAL_MAX_INT16)
            {
                bOverflow = true;
            }
        }
        else
        {
            break;
        }
        ++nPos;
    }

    if (io_rnPos == nPos) // read something?
    {
        o_rNumber = -1;
        return R_NOTHING;
    }

    io_rnPos = nPos;
    o_rNumber = nTemp;
    return (bOverflow) ? R_OVERFLOW : R_SUCCESS;
}

static bool
readDurationT(const ::rtl::OUString & rString, sal_Int32 & io_rnPos)
{
    if ((io_rnPos < rString.getLength()) &&
        (rString[io_rnPos] == sal_Unicode('T')))
    {
        ++io_rnPos;
        return true;
    }
    return false;
}

static bool
readDurationComponent(const ::rtl::OUString & rString,
    sal_Int32 & io_rnPos, sal_Int32 & io_rnTemp, bool & io_rbTimePart,
    sal_Int32 & o_rnTarget, const sal_Unicode c)
{
    if ((io_rnPos < rString.getLength()))
    {
        if (c == rString[io_rnPos])
        {
            ++io_rnPos;
            if (-1 != io_rnTemp)
            {
                o_rnTarget = io_rnTemp;
                io_rnTemp = -1;
                if (!io_rbTimePart)
                {
                    io_rbTimePart = readDurationT(rString, io_rnPos);
                }
                return (R_OVERFLOW !=
                        readUnsignedNumber(rString, io_rnPos, io_rnTemp));
            }
            else
            {
                return false;
            }
        }
    }
    return true;
}

/** convert ISO "duration" string to util::Duration */
bool Converter::convertDuration(util::Duration& rDuration,
                                const ::rtl::OUString& rString)
{
    const ::rtl::OUString string = rString.trim().toAsciiUpperCase();
    sal_Int32 nPos(0);

    bool bIsNegativeDuration(false);
    if (string.getLength() && (sal_Unicode('-') == string[0]))
    {
        bIsNegativeDuration = true;
        ++nPos;
    }

    if ((nPos < string.getLength())
        && (string[nPos] != sal_Unicode('P'))) // duration must start with "P"
    {
        return false;
    }

    ++nPos;

    /// last read number; -1 == no valid number! always reset after using!
    sal_Int32 nTemp(-1);
    bool bTimePart(false); // have we read 'T'?
    bool bSuccess(false);
    sal_Int32 nYears(0);
    sal_Int32 nMonths(0);
    sal_Int32 nDays(0);
    sal_Int32 nHours(0);
    sal_Int32 nMinutes(0);
    sal_Int32 nSeconds(0);
    sal_Int32 nMilliSeconds(0);

    bTimePart = readDurationT(string, nPos);
    bSuccess = (R_SUCCESS == readUnsignedNumber(string, nPos, nTemp));

    if (!bTimePart && bSuccess)
    {
        bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart,
                     nYears, sal_Unicode('Y'));
    }

    if (!bTimePart && bSuccess)
    {
        bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart,
                     nMonths, sal_Unicode('M'));
    }

    if (!bTimePart && bSuccess)
    {
        bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart,
                     nDays, sal_Unicode('D'));
    }

    if (bTimePart)
    {
        if (-1 == nTemp) // a 'T' must be followed by a component
        {
            bSuccess = false;
        }

        if (bSuccess)
        {
            bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart,
                         nHours, sal_Unicode('H'));
        }

        if (bSuccess)
        {
            bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart,
                         nMinutes, sal_Unicode('M'));
        }

        // eeek! seconds are icky.
        if ((nPos < string.getLength()) && bSuccess)
        {
            if (sal_Unicode('.') == string[nPos])
            {
                ++nPos;
                if (-1 != nTemp)
                {
                    nSeconds = nTemp;
                    nTemp = -1;
                    const sal_Int32 nStart(nPos);
                    bSuccess =
                        (R_NOTHING != readUnsignedNumber(string, nPos, nTemp));
                    if ((nPos < string.getLength()) && bSuccess)
                    {
                        if (-1 != nTemp)
                        {
                            nTemp = -1;
                            const sal_Int32 nDigits = nPos - nStart;
                            OSL_ENSURE(nDigits > 0, "bad code monkey");
                            const sal_Unicode cZero('0');
                            nMilliSeconds = 100 * (string[nStart] - cZero);
                            if (nDigits >= 2)
                            {
                                nMilliSeconds += 10 *
                                    (string[nStart+1] - cZero);
                                if (nDigits >= 3)
                                {
                                    nMilliSeconds += (string[nStart+2] - cZero);
                                }
                            }

                            if (sal_Unicode('S') == string[nPos])
                            {
                                ++nPos;
                            }
                            else
                            {
                                bSuccess = false;
                            }
                        }
                        else
                        {
                            bSuccess = false;
                        }
                    }
                }
                else
                {
                    bSuccess = false;
                }
            }
            else if (sal_Unicode('S') == string[nPos])
            {
                ++nPos;
                if (-1 != nTemp)
                {
                    nSeconds = nTemp;
                    nTemp = -1;
                }
                else
                {
                    bSuccess = false;
                }
            }
        }
    }

    if (nPos != string.getLength()) // string not processed completely?
    {
        bSuccess = false;
    }

    if (nTemp != -1) // unprocessed number?
    {
        bSuccess = false;
    }

    if (bSuccess)
    {
        rDuration.Negative      = bIsNegativeDuration;
        rDuration.Years         = static_cast<sal_Int16>(nYears);
        rDuration.Months        = static_cast<sal_Int16>(nMonths);
        rDuration.Days          = static_cast<sal_Int16>(nDays);
        rDuration.Hours         = static_cast<sal_Int16>(nHours);
        rDuration.Minutes       = static_cast<sal_Int16>(nMinutes);
        rDuration.Seconds       = static_cast<sal_Int16>(nSeconds);
        rDuration.MilliSeconds  = static_cast<sal_Int16>(nMilliSeconds);
    }

    return bSuccess;
}


/** convert util::Date to ISO "date" string */
void Converter::convertDate(
        ::rtl::OUStringBuffer& i_rBuffer,
        const util::Date& i_rDate)
{
    const util::DateTime dt(
            0, 0, 0, 0, i_rDate.Day, i_rDate.Month, i_rDate.Year);
    convertDateTime(i_rBuffer, dt, false);
}

/** convert util::DateTime to ISO "date" or "dateTime" string */
void Converter::convertDateTime(
        ::rtl::OUStringBuffer& i_rBuffer,
        const com::sun::star::util::DateTime& i_rDateTime,
        bool i_bAddTimeIf0AM )
{
    const sal_Unicode dash('-');
    const sal_Unicode col (':');
    const sal_Unicode dot ('.');
    const sal_Unicode zero('0');
    const sal_Unicode tee ('T');

    if (i_rDateTime.Year < 1000) {
        i_rBuffer.append(zero);
    }
    if (i_rDateTime.Year < 100) {
        i_rBuffer.append(zero);
    }
    if (i_rDateTime.Year < 10) {
        i_rBuffer.append(zero);
    }
    i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Year)  ).append(dash);
    if( i_rDateTime.Month < 10 ) {
        i_rBuffer.append(zero);
    }
    i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Month) ).append(dash);
    if( i_rDateTime.Day   < 10 ) {
        i_rBuffer.append(zero);
    }
    i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Day)   );

    if( i_rDateTime.Seconds != 0 ||
        i_rDateTime.Minutes != 0 ||
        i_rDateTime.Hours   != 0 ||
        i_bAddTimeIf0AM )
    {
        i_rBuffer.append(tee);
        if( i_rDateTime.Hours   < 10 ) {
            i_rBuffer.append(zero);
        }
        i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Hours)   )
                 .append(col);
        if( i_rDateTime.Minutes < 10 ) {
            i_rBuffer.append(zero);
        }
        i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Minutes) )
                 .append(col);
        if( i_rDateTime.Seconds < 10 ) {
            i_rBuffer.append(zero);
        }
        i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Seconds) );
        if( i_rDateTime.HundredthSeconds > 0 ) {
            i_rBuffer.append(dot);
            if( i_rDateTime.HundredthSeconds < 10 ) {
                i_rBuffer.append(zero);
            }
            i_rBuffer.append(
                static_cast<sal_Int32>(i_rDateTime.HundredthSeconds) );
        }
    }
}

/** convert ISO "date" or "dateTime" string to util::DateTime */
bool Converter::convertDateTime( util::DateTime& rDateTime,
                                 const ::rtl::OUString& rString )
{
    bool isDateTime;
    util::Date date;
    if (convertDateOrDateTime(date, rDateTime, isDateTime, rString))
    {
        if (!isDateTime)
        {
            rDateTime.Year = date.Year;
            rDateTime.Month = date.Month;
            rDateTime.Day = date.Day;
            rDateTime.Hours = 0;
            rDateTime.Minutes = 0;
            rDateTime.Seconds = 0;
            rDateTime.HundredthSeconds = 0;
        }
        return true;
    }
    else
    {
        return false;
    }
}

static bool
readDateTimeComponent(const ::rtl::OUString & rString,
    sal_Int32 & io_rnPos, sal_Int32 & o_rnTarget,
    const sal_Int32 nMinLength, const bool bExactLength)
{
    const sal_Int32 nOldPos(io_rnPos);
    sal_Int32 nTemp(0);
    if (R_SUCCESS != readUnsignedNumber(rString, io_rnPos, nTemp))
    {
        return false;
    }
    const sal_Int32 nTokenLength(io_rnPos - nOldPos);
    if ((nTokenLength < nMinLength) ||
        (bExactLength && (nTokenLength > nMinLength)))
    {
        return false; // bad length
    }
    o_rnTarget = nTemp;
    return true;
}

static bool lcl_isLeapYear(const sal_uInt32 nYear)
{
    return (((nYear % 4 == 0) && (nYear % 100 != 0)) ||
	(nYear % 400 == 0));
}

static sal_uInt16
lcl_MaxDaysPerMonth(const sal_Int32 nMonth, const sal_Int32 nYear)
{
    static sal_uInt16 s_MaxDaysPerMonth[12] =
        { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    OSL_ASSERT(0 < nMonth && nMonth <= 12);
    if ((2 == nMonth) && lcl_isLeapYear(nYear))
    {
        return 29;
    }
    return s_MaxDaysPerMonth[nMonth - 1];
}

/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
bool Converter::convertDateOrDateTime(
                util::Date & rDate, util::DateTime & rDateTime,
                bool & rbDateTime, const ::rtl::OUString & rString )
{
    bool bSuccess = true;

    const ::rtl::OUString string = rString.trim().toAsciiUpperCase();
    sal_Int32 nPos(0);
    bool bNegative(false);
    if ((string.getLength() > nPos) && (sal_Unicode('-') == string[nPos]))
    {
        ++nPos;
        bNegative = true;
    }

    sal_Int32 nYear(0);
    {
        bSuccess = readDateTimeComponent(string, nPos, nYear, 4, false);
        bSuccess &= (0 < nYear);
        bSuccess &= (nPos < string.getLength()); // not last token
    }
    if (bSuccess && (sal_Unicode('-') != string[nPos])) // separator
    {
        bSuccess = false;
    }
    if (bSuccess)
    {
        ++nPos;
    }

    sal_Int32 nMonth(0);
    if (bSuccess)
    {
        bSuccess = readDateTimeComponent(string, nPos, nMonth, 2, true);
        bSuccess &= (0 < nMonth) && (nMonth <= 12);
        bSuccess &= (nPos < string.getLength()); // not last token
    }
    if (bSuccess && (sal_Unicode('-') != string[nPos])) // separator
    {
        bSuccess = false;
    }
    if (bSuccess)
    {
        ++nPos;
    }

    sal_Int32 nDay(0);
    if (bSuccess)
    {
        bSuccess = readDateTimeComponent(string, nPos, nDay, 2, true);
        bSuccess &= (0 < nDay) && (nDay <= lcl_MaxDaysPerMonth(nMonth, nYear));
    }

    bool bHaveTime(false);
    if (bSuccess && (nPos < string.getLength()))
    {
        if (sal_Unicode('T') == string[nPos]) // time separator
        {
            bHaveTime = true;
            ++nPos;
        }
    }

    sal_Int32 nHours(0);
    sal_Int32 nMinutes(0);
    sal_Int32 nSeconds(0);
    sal_Int32 nMilliSeconds(0);
    if (bSuccess && bHaveTime)
    {
        {
            bSuccess = readDateTimeComponent(string, nPos, nHours, 2, true);
            bSuccess &= (0 <= nHours) && (nHours <= 24);
            bSuccess &= (nPos < string.getLength()); // not last token
        }
        if (bSuccess && (sal_Unicode(':') != string[nPos])) // separator
        {
            bSuccess = false;
        }
        if (bSuccess)
        {
            ++nPos;
        }

        if (bSuccess)
        {
            bSuccess = readDateTimeComponent(string, nPos, nMinutes, 2, true);
            bSuccess &= (0 <= nMinutes) && (nMinutes < 60);
            bSuccess &= (nPos < string.getLength()); // not last token
        }
        if (bSuccess && (sal_Unicode(':') != string[nPos])) // separator
        {
            bSuccess = false;
        }
        if (bSuccess)
        {
            ++nPos;
        }

        if (bSuccess)
        {
            bSuccess = readDateTimeComponent(string, nPos, nSeconds, 2, true);
            bSuccess &= (0 <= nSeconds) && (nSeconds < 60);
        }
        if (bSuccess && (nPos < string.getLength()) &&
            (sal_Unicode('.') == string[nPos])) // fraction separator
        {
            ++nPos;
            const sal_Int32 nStart(nPos);
            sal_Int32 nTemp(0);
            if (R_NOTHING == readUnsignedNumber(string, nPos, nTemp))
            {
                bSuccess = false;
            }
            if (bSuccess)
            {
                // cannot use nTemp because of possible leading zeros
                // and possible overflow => read digits directly
                const sal_Int32 nDigits(nPos - nStart);
                OSL_ENSURE(nDigits > 0, "bad code monkey");
                const sal_Unicode cZero('0');
                nMilliSeconds = 100 * (string[nStart] - cZero);
                if (nDigits >= 2)
                {
                    nMilliSeconds += 10 * (string[nStart+1] - cZero);
                    if (nDigits >= 3)
                    {
                        nMilliSeconds += (string[nStart+2] - cZero);
                    }
                }
            }
        }

        if (bSuccess && (nHours == 24))
        {
            if (!((0 == nMinutes) && (0 == nSeconds) && (0 == nMilliSeconds)))
            {
                bSuccess = false; // only 24:00:00 is valid
            }
#if 0
            else
            {
                nHours = 0; // normalize 24:00:00 to 00:00:00 of next day
                lcl_addDay(bNegative, nYear, nMonth, nDay, 1);
            }
#endif
        }
    }

    bool bHaveTimezone(false);
    bool bHaveTimezonePlus(false);
    bool bHaveTimezoneMinus(false);
    if (bSuccess && (nPos < string.getLength()))
    {
        const sal_Unicode c(string[nPos]);
        if (sal_Unicode('+') == c)
        {
            bHaveTimezone = true;
            bHaveTimezonePlus = true;
            ++nPos;
        }
        else if (sal_Unicode('-') == c)
        {
            bHaveTimezone = true;
            bHaveTimezoneMinus = true;
            ++nPos;
        }
        else if (sal_Unicode('Z') == c)
        {
            bHaveTimezone = true;
            ++nPos;
        }
        else
        {
            bSuccess = false;
        }
    }
    sal_Int32 nTimezoneHours(0);
    sal_Int32 nTimezoneMinutes(0);
    if (bSuccess && (bHaveTimezonePlus || bHaveTimezoneMinus))
    {
        bSuccess = readDateTimeComponent(
                        string, nPos, nTimezoneHours, 2, true);
        bSuccess &= (0 <= nTimezoneHours) && (nTimezoneHours <= 14);
        bSuccess &= (nPos < string.getLength()); // not last token
        if (bSuccess && (sal_Unicode(':') != string[nPos])) // separator
        {
            bSuccess = false;
        }
        if (bSuccess)
        {
            ++nPos;
        }
        if (bSuccess)
        {
            bSuccess = readDateTimeComponent(
                        string, nPos, nTimezoneMinutes, 2, true);
            bSuccess &= (0 <= nTimezoneMinutes) && (nTimezoneMinutes < 60);
        }
        if (bSuccess && (nTimezoneHours == 14))
        {
            if (0 != nTimezoneMinutes)
            {
                bSuccess = false; // only +-14:00 is valid
            }
        }
    }

    bSuccess &= (nPos == string.getLength()); // trailing junk?

    if (bSuccess && bHaveTimezone)
    {
        // util::DateTime does not support timezones!
#if 0
        // do not add timezone, just strip it (as suggested by er)
        lcl_addTimezone(bNegative, nYear, nMonth, nDay, nHours, nMinutes,
                !bHaveTimezoneMinus, nTimezoneHours, nTimezoneMinutes);
#endif
    }

    if (bSuccess)
    {
        if (bHaveTime) // time is optional
        {
            // util::DateTime does not support negative years!
            rDateTime.Year = static_cast<sal_uInt16>(nYear);
            rDateTime.Month = static_cast<sal_uInt16>(nMonth);
            rDateTime.Day = static_cast<sal_uInt16>(nDay);
            rDateTime.Hours = static_cast<sal_uInt16>(nHours);
            rDateTime.Minutes = static_cast<sal_uInt16>(nMinutes);
            rDateTime.Seconds = static_cast<sal_uInt16>(nSeconds);
            // util::DateTime does not support 3 decimal digits of precision!
            rDateTime.HundredthSeconds =
                static_cast<sal_uInt16>(nMilliSeconds / 10);
            rbDateTime = true;
        }
        else
        {
            rDate.Year = static_cast<sal_uInt16>(nYear);
            rDate.Month = static_cast<sal_uInt16>(nMonth);
            rDate.Day = static_cast<sal_uInt16>(nDay);
            rbDateTime = false;
        }
    }
    return bSuccess;
}


/** gets the position of the first comma after npos in the string
    rStr. Commas inside '"' pairs are not matched */
sal_Int32 Converter::indexOfComma( const OUString& rStr,
                                            sal_Int32 nPos )
{
    sal_Unicode cQuote = 0;
    sal_Int32 nLen = rStr.getLength();
    for( ; nPos < nLen; nPos++ )
    {
        sal_Unicode c = rStr[nPos];
        switch( c )
        {
        case sal_Unicode('\''):
            if( 0 == cQuote )
                cQuote = c;
            else if( '\'' == cQuote )
                cQuote = 0;
            break;

        case sal_Unicode('"'):
            if( 0 == cQuote )
                cQuote = c;
            else if( '\"' == cQuote )
                cQuote = 0;
            break;

        case sal_Unicode(','):
            if( 0 == cQuote )
                return nPos;
            break;
        }
    }

    return -1;
}

const
  sal_Char aBase64EncodeTable[] =
    { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
      'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
      'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };

const
  sal_uInt8 aBase64DecodeTable[]  =
    {											 62,255,255,255, 63, // 43-47
//                                                +               /

     52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255, // 48-63
//    0   1   2   3   4   5   6   7   8   9               =

    255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, // 64-79
//        A   B   C   D   E   F   G   H   I   J   K   L   M   N   O

     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, // 80-95
//    P   Q   R   S   T   U   V   W   X   Y   Z

      0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
//        a   b   c   d   e   f   g   h   i   j   k   l   m   n   o

     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; // 112-123
//    p   q   r   s   t   u   v   w   x   y   z



void ThreeByteToFourByte (const sal_Int8* pBuffer, const sal_Int32 nStart, const sal_Int32 nFullLen, rtl::OUStringBuffer& sBuffer)
{
    sal_Int32 nLen(nFullLen - nStart);
    if (nLen > 3)
        nLen = 3;
    if (nLen == 0)
    {
        sBuffer.setLength(0);
        return;
    }

    sal_Int32 nBinaer;
    switch (nLen)
    {
        case 1:
        {
            nBinaer = ((sal_uInt8)pBuffer[nStart + 0]) << 16;
        }
        break;
        case 2:
        {
            nBinaer = (((sal_uInt8)pBuffer[nStart + 0]) << 16) +
                    (((sal_uInt8)pBuffer[nStart + 1]) <<  8);
        }
        break;
        default:
        {
            nBinaer = (((sal_uInt8)pBuffer[nStart + 0]) << 16) +
                    (((sal_uInt8)pBuffer[nStart + 1]) <<  8) +
                    ((sal_uInt8)pBuffer[nStart + 2]);
        }
        break;
    }

    sBuffer.appendAscii("====");

    sal_uInt8 nIndex (static_cast<sal_uInt8>((nBinaer & 0xFC0000) >> 18));
    sBuffer.setCharAt(0, aBase64EncodeTable [nIndex]);

    nIndex = static_cast<sal_uInt8>((nBinaer & 0x3F000) >> 12);
    sBuffer.setCharAt(1, aBase64EncodeTable [nIndex]);
    if (nLen == 1)
        return;

    nIndex = static_cast<sal_uInt8>((nBinaer & 0xFC0) >> 6);
    sBuffer.setCharAt(2, aBase64EncodeTable [nIndex]);
    if (nLen == 2)
        return;

    nIndex = static_cast<sal_uInt8>((nBinaer & 0x3F));
    sBuffer.setCharAt(3, aBase64EncodeTable [nIndex]);
}

void Converter::encodeBase64(rtl::OUStringBuffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass)
{
	sal_Int32 i(0);
	sal_Int32 nBufferLength(aPass.getLength());
	const sal_Int8* pBuffer = aPass.getConstArray();
	while (i < nBufferLength)
	{
		rtl::OUStringBuffer sBuffer;
		ThreeByteToFourByte (pBuffer, i, nBufferLength, sBuffer);
		aStrBuffer.append(sBuffer);
		i += 3;
	}
}

void Converter::decodeBase64(uno::Sequence<sal_Int8>& aBuffer, const rtl::OUString& sBuffer)
{
#if OSL_DEBUG_LEVEL > 0
	sal_Int32 nCharsDecoded =
#endif
	decodeBase64SomeChars( aBuffer, sBuffer );
	OSL_ENSURE( nCharsDecoded == sBuffer.getLength(), "some bytes left in base64 decoding!" );
}

sal_Int32 Converter::decodeBase64SomeChars(
		uno::Sequence<sal_Int8>& rOutBuffer,
		const rtl::OUString& rInBuffer)
{
	sal_Int32 nInBufferLen = rInBuffer.getLength();
	sal_Int32 nMinOutBufferLen = (nInBufferLen / 4) * 3;
	if( rOutBuffer.getLength() < nMinOutBufferLen )
		rOutBuffer.realloc( nMinOutBufferLen );

	const sal_Unicode *pInBuffer = rInBuffer.getStr();
	sal_Int8 *pOutBuffer = rOutBuffer.getArray();
	sal_Int8 *pOutBufferStart = pOutBuffer;
	sal_Int32 nCharsDecoded = 0;

	sal_uInt8 aDecodeBuffer[4];
	sal_Int32 nBytesToDecode = 0;
	sal_Int32 nBytesGotFromDecoding = 3;
	sal_Int32 nInBufferPos= 0;
	while( nInBufferPos < nInBufferLen )
	{
		sal_Unicode cChar = *pInBuffer;
		if( cChar >= '+' && cChar <= 'z' )
		{
			sal_uInt8 nByte = aBase64DecodeTable[cChar-'+'];
			if( nByte != 255 )
			{
				// We have found a valid character!
				aDecodeBuffer[nBytesToDecode++] = nByte;

				// One '=' character at the end means 2 out bytes
				// Two '=' characters at the end mean 1 out bytes
				if( '=' == cChar && nBytesToDecode > 2 )
					nBytesGotFromDecoding--;
				if( 4 == nBytesToDecode )
				{
					// Four characters found, so we may convert now!
					sal_uInt32 nOut = (aDecodeBuffer[0] << 18) +
									  (aDecodeBuffer[1] << 12) +
									  (aDecodeBuffer[2] << 6) +
									   aDecodeBuffer[3];

					*pOutBuffer++  = (sal_Int8)((nOut & 0xff0000) >> 16);
					if( nBytesGotFromDecoding > 1 )
						*pOutBuffer++  = (sal_Int8)((nOut & 0xff00) >> 8);
					if( nBytesGotFromDecoding > 2 )
						*pOutBuffer++  = (sal_Int8)(nOut & 0xff);
					nCharsDecoded = nInBufferPos + 1;
					nBytesToDecode = 0;
					nBytesGotFromDecoding = 3;
				}
			}
			else
			{
				nCharsDecoded++;
			}
		}
		else
		{
			nCharsDecoded++;
		}

		nInBufferPos++;
		pInBuffer++;
	}
	if( (pOutBuffer - pOutBufferStart) != rOutBuffer.getLength() )
		rOutBuffer.realloc( pOutBuffer - pOutBufferStart );

	return nCharsDecoded;
}

void Converter::clearUndefinedChars(rtl::OUString& rTarget, const rtl::OUString& rSource)
{
	sal_uInt32 nLength(rSource.getLength());
	rtl::OUStringBuffer sBuffer(nLength);
	for (sal_uInt32 i = 0; i < nLength; i++)
	{
		sal_Unicode cChar = rSource[i];
		if (!(cChar < 0x0020) ||
			(cChar == 0x0009) ||		// TAB
			(cChar == 0x000A) ||		// LF
			(cChar == 0x000D))			// legal character
			sBuffer.append(cChar);
	}
	rTarget = sBuffer.makeStringAndClear();
}

double Converter::GetConversionFactor(::rtl::OUStringBuffer& rUnit, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit)
{
	double fRetval(1.0);
	rUnit.setLength(0L);

	const sal_Char* psUnit = 0;

	if(nSourceUnit != nTargetUnit)
	{
		switch(nSourceUnit)
		{
			case MeasureUnit::TWIP:
			{
				switch(nTargetUnit)
				{
					case MeasureUnit::MM_100TH:
					case MeasureUnit::MM_10TH:
					{
                        OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for twip values");
					}
					case MeasureUnit::MM:
					{
						// 0.01mm = 0.57twip (exactly)
						fRetval = ((25400.0 / 1440.0) / 1000.0);
						psUnit = gpsMM;
						break;
					}
					case MeasureUnit::CM:
					{
						// 0.001cm = 0.57twip (exactly)
						fRetval = ((25400.0 / 1440.0) / 10000.0);
						psUnit = gpsCM;
						break;
					}
					case MeasureUnit::POINT:
					{
						// 0.01pt = 0.2twip (exactly)
						fRetval = ((1000.0 / 20.0) / 1000.0);
						psUnit = gpsPT;
						break;
					}
					case MeasureUnit::INCH:
					default:
					{
                        OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for twip values");
						// 0.0001in = 0.144twip (exactly)
						fRetval = ((100000.0 / 1440.0) / 100000.0);
						psUnit = gpsINCH;
						break;
					}
				}
				break;
			}
			case MeasureUnit::POINT:
			{
                switch(nTargetUnit)
                {
                    case MeasureUnit::MM:
                        // 1mm = 72 / 25.4 pt (exactly)
                        fRetval = ( 25.4 / 72.0 );
                        psUnit = gpsMM;
                        break;

                    case MeasureUnit::CM:
                        // 1cm = 72 / 2.54 pt (exactly)
                        fRetval = ( 2.54 / 72.0 );
                        psUnit = gpsCM;
                        break;

                    case MeasureUnit::TWIP:
                        // 1twip = 72 / 1440 pt (exactly)
                        fRetval = 20.0;     // 1440.0 / 72.0
                        psUnit = gpsPC;
                        break;

                    case MeasureUnit::INCH:
                    default:
                        OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for pt values");
                        // 1in = 72 pt (exactly)
                        fRetval = ( 1.0 / 72.0 );
                        psUnit = gpsINCH;
                        break;
                }
                break;
			}
            case MeasureUnit::MM_10TH:
			{
				switch(nTargetUnit)
				{
					case MeasureUnit::MM_100TH:
					case MeasureUnit::MM_10TH:
					{
                        OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values");
					}
					case MeasureUnit::MM:
					{
						// 0.01mm = 1 mm/100 (exactly)
						fRetval = ((10.0 / 1.0) / 100.0);
						psUnit = gpsMM;
						break;
					}
					case MeasureUnit::CM:
					{
						// 0.001mm = 1 mm/100 (exactly)
						fRetval = ((10.0 / 1.0) / 1000.0);
						psUnit = gpsCM;
						break;
					}
					case MeasureUnit::POINT:
					{
						// 0.01pt = 0.35 mm/100 (exactly)
						fRetval = ((72000.0 / 2540.0) / 100.0);
						psUnit = gpsPT;
						break;
					}
					case MeasureUnit::INCH:
					default:
					{
                        OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values");
						// 0.0001in = 0.254 mm/100 (exactly)
						fRetval = ((100000.0 / 2540.0) / 10000.0);
						psUnit = gpsINCH;
						break;
					}
				}
				break;
            }
			case MeasureUnit::MM_100TH:
			{
				switch(nTargetUnit)
				{
					case MeasureUnit::MM_100TH:
					case MeasureUnit::MM_10TH:
					{
                        OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values");
					}
					case MeasureUnit::MM:
					{
						// 0.01mm = 1 mm/100 (exactly)
						fRetval = ((10.0 / 1.0) / 1000.0);
						psUnit = gpsMM;
						break;
					}
					case MeasureUnit::CM:
					{
						// 0.001mm = 1 mm/100 (exactly)
						fRetval = ((10.0 / 1.0) / 10000.0);
						psUnit = gpsCM;
						break;
					}
					case MeasureUnit::POINT:
					{
						// 0.01pt = 0.35 mm/100 (exactly)
						fRetval = ((72000.0 / 2540.0) / 1000.0);
						psUnit = gpsPT;
						break;
					}
					case MeasureUnit::INCH:
					default:
					{
                        OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values");
						// 0.0001in = 0.254 mm/100 (exactly)
						fRetval = ((100000.0 / 2540.0) / 100000.0);
						psUnit = gpsINCH;
						break;
					}
				}
				break;
			}
		}

		if( psUnit )
			rUnit.appendAscii( psUnit );
	}

	return fRetval;
}

sal_Int16 Converter::GetUnitFromString(const ::rtl::OUString& rString, sal_Int16 nDefaultUnit)
{
	sal_Int32 nPos = 0L;
	sal_Int32 nLen = rString.getLength();
	sal_Int16 nRetUnit = nDefaultUnit;

	// skip white space
	while( nPos < nLen && sal_Unicode(' ') == rString[nPos] )
		nPos++;

	// skip negative
	if( nPos < nLen && sal_Unicode('-') == rString[nPos] )
		nPos++;

	// skip number
	while( nPos < nLen && sal_Unicode('0') <= rString[nPos] && sal_Unicode('9') >= rString[nPos] )
		nPos++;

	if( nPos < nLen && sal_Unicode('.') == rString[nPos] )
	{
		nPos++;
		while( nPos < nLen && sal_Unicode('0') <= rString[nPos] && sal_Unicode('9') >= rString[nPos] )
			nPos++;
	}

	// skip white space
	while( nPos < nLen && sal_Unicode(' ') == rString[nPos] )
		nPos++;

	if( nPos < nLen )
	{
		switch(rString[nPos])
		{
			case sal_Unicode('%') :
			{
				nRetUnit = MeasureUnit::PERCENT;
				break;
			}
			case sal_Unicode('c'):
			case sal_Unicode('C'):
			{
				if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('m')
					|| rString[nPos+1] == sal_Unicode('M')))
					nRetUnit = MeasureUnit::CM;
				break;
			}
			case sal_Unicode('e'):
			case sal_Unicode('E'):
			{
				// CSS1_EMS or CSS1_EMX later
				break;
			}
			case sal_Unicode('i'):
			case sal_Unicode('I'):
			{
				if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('n')
					|| rString[nPos+1] == sal_Unicode('n')))
					nRetUnit = MeasureUnit::INCH;
				break;
			}
			case sal_Unicode('m'):
			case sal_Unicode('M'):
			{
				if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('m')
					|| rString[nPos+1] == sal_Unicode('M')))
					nRetUnit = MeasureUnit::MM;
				break;
			}
			case sal_Unicode('p'):
			case sal_Unicode('P'):
			{
				if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('t')
					|| rString[nPos+1] == sal_Unicode('T')))
					nRetUnit = MeasureUnit::POINT;
				if(nPos+1 < nLen && (rString[nPos+1] == sal_Unicode('c')
					|| rString[nPos+1] == sal_Unicode('C')))
					nRetUnit = MeasureUnit::TWIP;
				break;
			}
		}
	}

	return nRetUnit;
}

}
