blob: ddfda8280bcdab55340432f0e723d4c8af44a99d [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache\@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation, and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.ibm.com . For more information
* on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/**
* $Log$
* Revision 1.1 1999/11/09 01:05:51 twl
* Initial revision
*
* Revision 1.2 1999/11/08 20:45:20 rahul
* Swat for adding in Product name and CVS comment log variable.
*
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <memory.h>
#include <string.h>
#include <util/ArrayIndexOutOfBoundsException.hpp>
#include <util/IllegalArgumentException.hpp>
#include <util/Janitor.hpp>
#include <util/PlatformUtils.hpp>
#include <util/StdOut.hpp>
#include <util/RuntimeException.hpp>
#include <util/TransService.hpp>
#include <util/TranscodingException.hpp>
#include <util/ValueArrayOf.hpp>
#include <util/XMLString.hpp>
#include <util/XMLUni.hpp>
// ---------------------------------------------------------------------------
// Local static data
//
// gConverter
// This is initialized when the user calls the platform init method,
// which calls our init method. This is the converter used for default
// conversion to/from the local code page.
// ---------------------------------------------------------------------------
static XMLTranscoder* gTranscoder = 0;
static XMLCh gNullStr[] =
{
chOpenCurly, chLatin_n, chLatin_u, chLatin_l, chLatin_l, chCloseCurly, chNull
};
// ---------------------------------------------------------------------------
// XMLString: Public static methods
// ---------------------------------------------------------------------------
void XMLString::binToText( const unsigned long toFormat
, char* const toFill
, const unsigned int maxChars
, const unsigned int radix)
{
static const char digitList[16] =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
, 'A', 'B', 'C', 'D', 'E', 'F'
};
if (!maxChars)
ThrowXML(IllegalArgumentException, XML4CExcepts::Str_ZeroSizedTargetBuf);
// Handle special case
if (!toFormat)
{
toFill[0] = '0';
toFill[1] = 0;
return;
}
// This is used to fill the temp buffer
unsigned int tmpIndex = 0;
// A copy of the conversion value that we can modify
unsigned int tmpVal = toFormat;
//
// Convert into a temp buffer that we know is large enough. This avoids
// having to check for overflow in the inner loops, and we have to flip
// the resulting XMLString anyway.
//
char tmpBuf[128];
//
// For each radix, do the optimal thing. For bin and hex, we can special
// case them and do shift and mask oriented stuff. For oct and decimal
// there isn't much to do but bull through it with divides.
//
if (radix == 2)
{
while (tmpVal)
{
if (tmpVal & 0x1UL)
tmpBuf[tmpIndex++] = '1';
else
tmpBuf[tmpIndex++] = '0';
tmpVal >>= 1;
}
}
else if (radix == 16)
{
while (tmpVal)
{
const unsigned int charInd = (tmpVal & 0xFUL);
tmpBuf[tmpIndex++] = digitList[charInd];
tmpVal >>= 4;
}
}
else if ((radix == 8) || (radix == 10))
{
while (tmpVal)
{
const unsigned int charInd = (tmpVal % radix);
tmpBuf[tmpIndex++] = digitList[charInd];
tmpVal /= radix;
}
}
else
{
ThrowXML(RuntimeException, XML4CExcepts::Str_UnknownRadix);
}
// See if have enough room in the caller's buffer
if (tmpIndex > maxChars)
ThrowXML(IllegalArgumentException, XML4CExcepts::Str_TargetBufTooSmall);
// Reverse the tmp buffer into the caller's buffer
unsigned int outIndex = 0;
for (; tmpIndex > 0; tmpIndex--)
toFill[outIndex++] = tmpBuf[tmpIndex-1];
// And cap off the caller's buffer
toFill[outIndex] = char(0);
}
void XMLString::binToText( const unsigned int toFormat
, char* const toFill
, const unsigned int maxChars
, const unsigned int radix)
{
// Just call the unsigned long version
binToText((unsigned long)toFormat, toFill, maxChars, radix);
}
void XMLString::binToText( const long toFormat
, char* const toFill
, const unsigned int maxChars
, const unsigned int radix)
{
//
// If its negative, then put a negative sign into the output and flip
// the sign of the local temp value.
//
unsigned int startInd = 0;
unsigned long actualVal;
if (toFormat < 0)
{
toFill[0] = '-';
startInd++;
actualVal = (unsigned long)(toFormat * -1);
}
else
{
actualVal = (unsigned long)(toFormat);
}
// And now call the unsigned long version
binToText(actualVal, &toFill[startInd], maxChars, radix);
}
void XMLString::binToText( const int toFormat
, char* const toFill
, const unsigned int maxChars
, const unsigned int radix)
{
//
// If its negative, then put a negative sign into the output and flip
// the sign of the local temp value.
//
unsigned int startInd = 0;
unsigned long actualVal;
if (toFormat < 0)
{
toFill[0] = '-';
startInd++;
actualVal = (unsigned long)(toFormat * -1);
}
else
{
actualVal = (unsigned long)(toFormat);
}
// And now call the unsigned long version
binToText(actualVal, &toFill[startInd], maxChars, radix);
}
void XMLString::catString(char* const target, const char* const src)
{
strcat(target, src);
}
int XMLString::compareIString(const char* const str1, const char* const str2)
{
return stricmp(str1, str2);
}
int XMLString::compareNString( const char* const str1
, const char* const str2
, const unsigned int count)
{
// Watch for pathological secenario
if (!count)
return 0;
return strncmp(str1, str2, count);
}
int XMLString::compareNIString( const char* const str1
, const char* const str2
, const unsigned int count)
{
if (!count)
return 0;
return strnicmp(str1, str2, count);
}
int XMLString::compareString( const char* const str1
, const char* const str2)
{
return strcmp(str1, str2);
}
void XMLString::copyString( char* const target
, const char* const src)
{
strcpy(target, src);
}
unsigned int XMLString::hash( const char* const tohash
, const unsigned int hashModulus)
{
unsigned int hashVal = 0;
const char* curCh = tohash;
while (*curCh)
{
unsigned int top = hashVal >> 24;
hashVal += (hashVal * 37) + top + (unsigned int)(*curCh);
curCh++;
}
// Divide by modulus
return hashVal % hashModulus;
}
int XMLString::indexOf(const char* const toSearch, const char ch)
{
const unsigned int len = strlen(toSearch);
for (unsigned int i = 0; i < len; i++)
{
if (toSearch[i] == ch)
return i;
}
return -1;
}
int XMLString::lastIndexOf(const char* const toSearch, const char ch)
{
const int len = strlen(toSearch);
for (int i = len-1; i >= 0; i--)
{
if (toSearch[i] == ch)
return i;
}
return -1;
}
int XMLString::lastIndexOf( const char* const toSearch
, const char ch
, const unsigned int fromIndex)
{
const int len = strlen(toSearch);
// Make sure the start index is within the XMLString bounds
if ((int)fromIndex > len-1)
ThrowXML(ArrayIndexOutOfBoundsException, XML4CExcepts::Str_StartIndexPastEnd);
for (int i = (int)fromIndex; i >= 0; i--)
{
if (toSearch[i] == ch)
return i;
}
return -1;
}
unsigned int XMLString::replaceTokens( XMLCh* const errText
, const unsigned int maxChars
, const XMLCh* const text1
, const XMLCh* const text2
, const XMLCh* const text3
, const XMLCh* const text4)
{
//
// We have to build the string back into the source string, so allocate
// a temp string and copy the orignal text to it. We'll then treat the
// incoming buffer as a target buffer. Put a janitor on it to make sure
// it gets cleaned up.
//
XMLCh* orgText = replicate(errText);
ArrayJanitor<XMLCh> janText(orgText);
XMLCh* pszSrc = orgText;
unsigned int curOutInd = 0;
while (*pszSrc && (curOutInd < maxChars))
{
//
// Loop until we see a { character. Until we do, just copy chars
// from src to target, being sure not to overrun the output buffer.
//
while ((*pszSrc != chOpenCurly) && (curOutInd < maxChars))
{
if (!*pszSrc)
break;
errText[curOutInd++] = *pszSrc++;
}
// If we did not find a curly, then we are done
if (*pszSrc != chOpenCurly)
break;
//
// Probe this one to see if it matches our pattern of {x}. If not
// then copy over those chars and go back to the first loop.
//
if ((*(pszSrc+1) >= chDigit_0)
&& (*(pszSrc+1) <= chDigit_3)
&& (*(pszSrc+2) == chCloseCurly))
{
//
// Its one of our guys, so move the source pointer up past the
// token we are replacing. First though get out the token number
// character.
//
XMLCh tokCh = *(pszSrc+1);
pszSrc += 3;
// Now copy over the replacement text
const XMLCh* repText = 0;
if (tokCh == chDigit_0)
repText = text1;
else if (tokCh == chDigit_1)
repText = text2;
else if (tokCh == chDigit_2)
repText = text3;
else if (tokCh == chDigit_3)
repText = text4;
// If this one is null, copy over a null string
if (!repText)
repText = gNullStr;
while (*repText && (curOutInd < maxChars))
errText[curOutInd++] = *repText++;
}
else
{
// Escape the curly brace character and continue
errText[curOutInd++] = *pszSrc++;
}
}
// Copy over a null terminator
errText[curOutInd] = 0;
// And return the count of chars we output
return curOutInd;
}
char* XMLString::replicate(const char* const toRep)
{
// If a null string, return a null string
if (!toRep)
return 0;
//
// Get the len of the source and allocate a new buffer. Make sure to
// account for the nul terminator.
//
const unsigned int srcLen = strlen(toRep);
char* ret = new char[srcLen+1];
// Copy over the text, adjusting for the size of a char
memcpy(ret, toRep, (srcLen+1) * sizeof(char));
return ret;
}
bool XMLString::startsWith(const char* const toTest, const char* const prefix)
{
return (strncmp(toTest, prefix, strlen(prefix)) == 0);
}
bool XMLString::startsWithI(const char* const toTest
, const char* const prefix)
{
return (strnicmp(toTest, prefix, strlen(prefix)) == 0);
}
unsigned int XMLString::stringLen(const char* const src)
{
return strlen(src);
}
char* XMLString::transcode(const XMLCh* const toTranscode)
{
return gTranscoder->transcode(toTranscode);
}
bool XMLString::transcode( const XMLCh* const toTranscode
, char* const toFill
, const unsigned int maxChars)
{
if (!gTranscoder->transcode(toTranscode, toFill, maxChars))
return false;
return true;
}
XMLCh* XMLString::transcode(const char* const toTranscode)
{
return gTranscoder->transcode(toTranscode);
}
bool XMLString::transcode( const char* const toTranscode
, XMLCh* const toFill
, const unsigned int maxChars)
{
if (!gTranscoder->transcode(toTranscode, toFill, maxChars))
return false;
return true;
}
void XMLString::trim(char* const toTrim)
{
const unsigned int len = strlen(toTrim);
unsigned int skip, scrape;
for (skip = 0; skip < len; skip++)
{
if (toTrim[skip] > ' ')
break;
}
for (scrape = len; scrape > skip; scrape--)
{
if (toTrim[scrape - 1] > ' ')
break;
}
// Cap off at the scrap point
if (scrape)
toTrim[scrape] = 0;
if (skip)
{
// Copy the chars down
unsigned int index = 0;
while (toTrim[skip])
toTrim[index++] = toTrim[skip++];
}
}
// ---------------------------------------------------------------------------
// Unicode versions of most of the string methods
// ---------------------------------------------------------------------------
void XMLString::binToText( const unsigned long toFormat
, XMLCh* const toFill
, const unsigned int maxChars
, const unsigned int radix)
{
static const XMLCh digitList[16] =
{
chDigit_0, chDigit_1, chDigit_2, chDigit_3, chDigit_4, chDigit_5
, chDigit_6, chDigit_7, chDigit_8, chDigit_9, chLatin_A, chLatin_B
, chLatin_C, chLatin_D, chLatin_e, chLatin_F
};
if (!maxChars)
ThrowXML(IllegalArgumentException, XML4CExcepts::Str_ZeroSizedTargetBuf);
// Handle special case
if (!toFormat)
{
toFill[0] = chDigit_0;
toFill[1] = chNull;
return;
}
// This is used to fill the temp buffer
unsigned int tmpIndex = 0;
// A copy of the conversion value that we can modify
unsigned int tmpVal = toFormat;
//
// Convert into a temp buffer that we know is large enough. This avoids
// having to check for overflow in the inner loops, and we have to flip
// the resulting sring anyway.
//
XMLCh tmpBuf[128];
//
// For each radix, do the optimal thing. For bin and hex, we can special
// case them and do shift and mask oriented stuff. For oct and decimal
// there isn't much to do but bull through it with divides.
//
if (radix == 2)
{
while (tmpVal)
{
if (tmpVal & 0x1UL)
tmpBuf[tmpIndex++] = chDigit_1;
else
tmpBuf[tmpIndex++] = chDigit_0;
tmpVal >>= 1;
}
}
else if (radix == 16)
{
while (tmpVal)
{
const unsigned int charInd = (tmpVal & 0xFUL);
tmpBuf[tmpIndex++] = digitList[charInd];
tmpVal >>= 4;
}
}
else if ((radix == 8) || (radix == 10))
{
while (tmpVal)
{
const unsigned int charInd = (tmpVal % radix);
tmpBuf[tmpIndex++] = digitList[charInd];
tmpVal /= radix;
}
}
else
{
ThrowXML(RuntimeException, XML4CExcepts::Str_UnknownRadix);
}
// See if have enough room in the caller's buffer
if (tmpIndex > maxChars)
ThrowXML(IllegalArgumentException, XML4CExcepts::Str_TargetBufTooSmall);
// Reverse the tmp buffer into the caller's buffer
unsigned int outIndex = 0;
for (; tmpIndex > 0; tmpIndex--)
toFill[outIndex++] = tmpBuf[tmpIndex-1];
// And cap off the caller's buffer
toFill[outIndex] = chNull;
}
void XMLString::binToText( const unsigned int toFormat
, XMLCh* const toFill
, const unsigned int maxChars
, const unsigned int radix)
{
// Just call the unsigned long version
binToText((unsigned long)toFormat, toFill, maxChars, radix);
}
void XMLString::binToText( const long toFormat
, XMLCh* const toFill
, const unsigned int maxChars
, const unsigned int radix)
{
//
// If its negative, then put a negative sign into the output and flip
// the sign of the local temp value.
//
unsigned int startInd = 0;
unsigned long actualVal;
if (toFormat < 0)
{
toFill[0] = chDash;
startInd++;
actualVal = (unsigned long)(toFormat * -1);
}
else
{
actualVal = (unsigned long)(toFormat);
}
// And now call the unsigned long version
binToText(actualVal, &toFill[startInd], maxChars, radix);
}
void XMLString::binToText( const int toFormat
, XMLCh* const toFill
, const unsigned int maxChars
, const unsigned int radix)
{
//
// If its negative, then put a negative sign into the output and flip
// the sign of the local temp value.
//
unsigned int startInd = 0;
unsigned long actualVal;
if (toFormat < 0)
{
toFill[0] = chDash;
startInd++;
actualVal = (unsigned long)(toFormat * -1);
}
else
{
actualVal = (unsigned long)(toFormat);
}
// And now call the unsigned long version
binToText(actualVal, &toFill[startInd], maxChars, radix);
}
void XMLString::catString(XMLCh* const target, const XMLCh* const src)
{
// Get the starting point for the cat on the target XMLString
unsigned int index = stringLen(target);
// While the source is not zero, add them to target and bump
const XMLCh* pszTmp = src;
while (*pszTmp)
target[index++] = *pszTmp++;
// Cap off the target where we ended
target[index] = chNull;
}
int XMLString::compareIString( const XMLCh* const str1
, const XMLCh* const str2)
{
return XMLPlatformUtils::fgTransService->compareIString(str1, str2);
}
int XMLString::compareNString( const XMLCh* const str1
, const XMLCh* const str2
, const unsigned int maxChars)
{
const XMLCh* psz1 = str1;
const XMLCh* psz2 = str2;
unsigned int curCount = 0;
while (true)
{
// If an inequality, then return difference
if (*psz1 != *psz2)
return int(*psz1) - int(*psz2);
// If either ended, then both ended, so equal
if (!*psz1 || !*psz2)
break;
// Move upwards to next chars
psz1++;
psz2++;
//
// Bump the count of chars done. If it equals the max then we are
// equal for the requested count, so break out and return equal.
//
curCount++;
if (curCount == maxChars)
break;
}
return 0;
}
int XMLString::compareNIString( const XMLCh* const str1
, const XMLCh* const str2
, const unsigned int maxChars)
{
return XMLPlatformUtils::fgTransService->compareNIString(str1, str2, maxChars);
}
int XMLString::compareString( const XMLCh* const str1
, const XMLCh* const str2)
{
const XMLCh* psz1 = str1;
const XMLCh* psz2 = str2;
while (true)
{
// If an inequality, then return the difference
if (*psz1 != *psz2)
return int(*psz1) - int(*psz2);
// If either has ended, then they both ended, so equal
if (!*psz1 || !*psz2)
break;
// Move upwards for the next round
psz1++;
psz2++;
}
return 0;
}
void XMLString::copyString(XMLCh* const target, const XMLCh* const src)
{
if (!src)
{
*target = 0;
return;
}
XMLCh* pszOut = target;
const XMLCh* pszIn = src;
while (*pszIn)
*pszOut++ = *pszIn++;
// Capp off the target where we ended
*pszOut = 0;
}
unsigned int XMLString::hash( const XMLCh* const tohash
, const unsigned int hashModulus)
{
unsigned int hashVal = 0;
const XMLCh* curCh = tohash;
while (*curCh)
{
unsigned int top = hashVal >> 24;
hashVal += (hashVal * 37) + top + (unsigned int)(*curCh);
curCh++;
}
// Divide by modulus
return hashVal % hashModulus;
}
unsigned int XMLString::hashN( const XMLCh* const tohash
, const unsigned int n
, const unsigned int hashModulus)
{
unsigned int hashVal = 0;
const XMLCh* curCh = tohash;
int i = n;
while (i--)
{
unsigned int top = hashVal >> 24;
hashVal += (hashVal * 37) + top + (unsigned int)(*curCh);
curCh++;
}
// Divide by modulus
return hashVal % hashModulus;
}
int XMLString::indexOf(const XMLCh* const toSearch, const XMLCh ch)
{
const unsigned int len = stringLen(toSearch);
for (unsigned int i = 0; i < len; i++)
{
if (toSearch[i] == ch)
return i;
}
return -1;
}
int XMLString::lastIndexOf(const XMLCh* const toSearch, const XMLCh ch)
{
const int len = stringLen(toSearch);
for (int i = len-1; i >= 0; i--)
{
if (toSearch[i] == ch)
return i;
}
return -1;
}
int XMLString::lastIndexOf( const XMLCh* const toSearch
, const XMLCh ch
, const unsigned int fromIndex)
{
const int len = stringLen(toSearch);
if ((int)fromIndex > len-1)
ThrowXML(ArrayIndexOutOfBoundsException, XML4CExcepts::Str_StartIndexPastEnd);
for (int i = (int)fromIndex; i >= 0; i--)
{
if (toSearch[i] == ch)
return i;
}
return -1;
}
XMLCh*
XMLString::makeUName(const XMLCh* const pszURI, const XMLCh* const pszName)
{
//
// If there is a URI, then format out the full name in the {uri}name
// form. Otherwise, just set it to the same thing as the base name.
//
XMLCh* pszRet = 0;
const unsigned int uriLen = stringLen(pszURI);
if (uriLen)
{
pszRet = new XMLCh[uriLen + stringLen(pszName) + 3];
XMLCh szTmp[2];
szTmp[1] = 0;
szTmp[0] = chOpenCurly;
copyString(pszRet, szTmp);
catString(pszRet, pszURI);
szTmp[0] = chCloseCurly;
catString(pszRet, szTmp);
catString(pszRet, pszName);
}
else
{
pszRet = replicate(pszName);
}
return pszRet;
}
XMLCh* XMLString::replicate(const XMLCh* const toRep)
{
// If a null string, return a null string
if (!toRep)
return 0;
XMLCh* ret = new XMLCh[stringLen(toRep) + 1];
copyString(ret, toRep);
return ret;
}
bool XMLString::startsWith( const XMLCh* const toTest
, const XMLCh* const prefix)
{
return (compareNString(toTest, prefix, stringLen(prefix)) == 0);
}
bool XMLString::startsWithI(const XMLCh* const toTest
, const XMLCh* const prefix)
{
return (compareNIString(toTest, prefix, stringLen(prefix)) == 0);
}
unsigned int XMLString::stringLen(const XMLCh* const src)
{
if (!src)
return 0;
unsigned int len = 0;
const XMLCh* pszTmp = src;
while (*pszTmp++)
len++;
return len;
}
void XMLString::trim(XMLCh* const toTrim)
{
const unsigned int len = stringLen(toTrim);
unsigned int skip, scrape;
for (skip = 0; skip < len; skip++)
{
if (XMLPlatformUtils::fgTransService->isSpace(toTrim[skip]))
break;
}
for (scrape = len; scrape > skip; scrape--)
{
if (XMLPlatformUtils::fgTransService->isSpace(toTrim[scrape - 1]))
break;
}
// Cap off at the scrap point
if (scrape)
toTrim[scrape] = 0;
if (skip)
{
// Copy the chars down
unsigned int index = 0;
while (toTrim[skip])
toTrim[index++] = toTrim[skip++];
}
}
// ---------------------------------------------------------------------------
// XMLString: Private static methods
// ---------------------------------------------------------------------------
void XMLString::initString(XMLTranscoder* const defToUse)
{
// Store away the default transcoder that we are to use
gTranscoder = defToUse;
}