//
// Copyright (C) 2006-2011 SIPez LLC.  All rights reserved.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// Copyright (C) 2004-2006 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2004-2006 Pingtel Corp.  All rights reserved.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
///////////////////////////////////////////////////////////////////////////////


// SYSTEM INCLUDES
#include <assert.h>
#include <string.h>
#include <ctype.h>

// APPLICATION INCLUDES
#include "utl/UtlString.h"
#include "os/OsDefs.h"

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
#define UTLSTRING_MIN_INCREMENT 100 ///< smallest additional memory to be allocated 

// STATIC VARIABLE INITIALIZATIONS
const UtlContainableType UtlString::TYPE = "UtlString";
const char* UtlString::ssNull = "";
const size_t UtlString::UTLSTRING_NOT_FOUND = (size_t) (-1);
const size_t UtlString::UTLSTRING_TO_END = (size_t) (-1);

/* //////////////////////////// PUBLIC //////////////////////////////////// */

/* ============================ CREATORS ================================== */

// Default Constructor
UtlString::UtlString()
{
    mpData = mBuiltIn;
    mBuiltIn[0] = '\000';
    mSize = 0;
    mCapacity = DEFAULT_UTLSTRING_CAPACITY;
}


// Copy constructor
UtlString::UtlString(const char* szSource)
{
    mpData = mBuiltIn;
    mBuiltIn[0] = '\000';
    mSize = 0;
    mCapacity = DEFAULT_UTLSTRING_CAPACITY;
    append(szSource);
}

// Copy constructor
UtlString::UtlString(const char* szSource, size_t length)
{
    mpData = mBuiltIn;
    mBuiltIn[0] = '\000';
    mSize = 0;
    mCapacity = DEFAULT_UTLSTRING_CAPACITY;
    append(szSource, length);
}

// Copy constructor
UtlString::UtlString(const UtlString& source)
{
    mpData = mBuiltIn;
    mBuiltIn[0] = '\000';
    mSize = 0;
    mCapacity = DEFAULT_UTLSTRING_CAPACITY;
    capacity(source.mCapacity);
    append(source);
}


// Copy constructor
UtlString::UtlString(const UtlString& source, size_t length)
{
    mpData = mBuiltIn;
    mBuiltIn[0] = '\000';
    mSize = 0;
    mCapacity = DEFAULT_UTLSTRING_CAPACITY;
    capacity(source.mCapacity);

    // Check that we do not copy beyond the end of source
    if(length > source.mSize)
    {
        length = source.mSize;
    }
    append(source.mpData, length);
}

UtlCopyableContainable* UtlString::clone() const
{
   return new UtlString(*this);
}

// Destructor
UtlString::~UtlString()
{
    if(mpData && mpData != mBuiltIn)
    {
        delete[] mpData;
    }
    mpData = NULL;
    mCapacity = 0;
    mSize = 0;
}

/* ============================ MANIPULATORS ============================== */

// Assignment operator, use append(const char*).
UtlString& UtlString::operator=(const char* szStr)
{
   // check for self assignment
   if (mpData != szStr)
   {
      remove(0);
      if(szStr && *szStr)
      {
         append(szStr);
      }
   }

   return *this;
}


// Assignment operator, use append(const char* , size_t ).
UtlString& UtlString::operator=(const UtlString& str)
{
    if (this != &str)
    {
       remove(0);
       if(str.mCapacity > mCapacity)
       {
          capacity(str.mCapacity);
       }
       append(str.mpData, str.mSize);
    }

    return *this;
}


// Plus equals operator, use append(const char*).
UtlString& UtlString::operator+=(const char* szStr)
{
    return append(szStr);
}


// Plus equals operator, use append(const UtlString& ).
UtlString& UtlString::operator+=(const UtlString& str)
{
    return append(str);
}

// Plus equals operator, use append(const char).
UtlString& UtlString::operator+=(const char c)
{
    return append(c);
}

// Get the character at position N.
char UtlString::operator()(size_t N)
{
    char foundChar = '\0';

    if (mpData && N >= 0 && N < mSize)
    {
        foundChar = mpData[N];
    }

    return foundChar;
}


// prepend:prepend a copy of the null-terminated character string pointed
// to by szStr to self.Returns a reference to self.
// use append().
UtlString& UtlString::prepend(const char* szStr)
{
    UtlString newUtlString;

    //FIX: should avoid allocation if capacity is big enough
    newUtlString.append(szStr);
    newUtlString.append(mpData);
    remove(0);
    append(newUtlString);

    return *this ;
}


// Append the designated string to the end of this string.
// use append(const char* szStr, size_t N)
UtlString& UtlString::append(const char* szStr)
{
    if (szStr)
    {
        append(szStr, strlen(szStr));
    }

    return *this;
}


// Append the designated string to the end of this string.
// use append(const char* szStr, size_t N)
UtlString& UtlString::append(const char c)
{
    append(&c, 1);

    return *this;
}


// Append up to N bytes of the designated string to the end of this string.
UtlString& UtlString::append(const char* szStr, size_t N)
{
    // The N bytes to append may contain zero bytes.
    if(szStr && N>0)
    {
        // Calculate the total space needed, including room for a final
        // zero byte.
        size_t maxCap = mSize + N + 1;
        // If necessary, reallocate the data area to hold maxCap bytes.
        if (maxCap <= capacity(maxCap))
        {
            if (mpData)
            {
               // Copy the N bytes after the existing mSize bytes in the string.
               memcpy(&mpData[mSize], szStr, N);
               // Update the size of the string.
               mSize += N;
               // Append a final zero byte.
               mpData[mSize] = '\0';
            }
        }
        else
        {
            //allocate failure, do nothing;
        }
    }

    return *this;
}

UtlString& UtlString::appendFormat(const char* format, ...)
{     
   va_list args;
   va_start(args, format);

   int n;
   int extraSize = 0;

   for(;;)
   {
      size_t availableCap = capacity() - mSize;
      va_list tmp;

      /* Argument list must be copied, because we call vsnprintf in a loop
      * and first call will invalidate list, so on second iteration it
      * will contain junk. (this is not a problem for i386, but appears
      * as such on e.g. x86_64) */
      va_copy(tmp, args);
      /* Try to print in the allocated space. */
      n = vsnprintf (&mpData[mSize], availableCap, format, tmp);
      va_end(tmp);

      /* If that worked, return the string. */
      if (n > -1 && n < (int)availableCap)
      {
         break;
      }
      /* Else try again with more space. */
      if (n > -1)    /* glibc 2.1 */
         extraSize = n+1; /* precisely what is needed */
      else           /* glibc 2.0 */
         if (extraSize == 0)
            extraSize = availableCap;
         else
            extraSize *= 2;  /* twice the old size */

      capacity(mSize + extraSize);
   }

   mSize += n;

   va_end(args);

   return *this;
}

// Append the designated string to the end of this string.
// use append(const char* ).
UtlString& UtlString::append(const UtlString& str)
{
   append(str.mpData, str.mSize);

   return *this ;
}

// Append the designated string to the end of this string.
// use append(const char* ).
UtlString& UtlString::append(const UtlString& str, size_t position, size_t length)
{
   if (position < str.mSize)
   {
      if (   (length == UTLSTRING_TO_END)
          || (position+length > str.mSize)
          )
      {
         length = str.mSize - position;
      }

      append(str.mpData+position, length);
   }
   
   return *this;
}

UtlString& UtlString::insert(size_t position, const char* source)
{
   if(source && position < mSize)
   {
      size_t len = strlen(source);
      insert(position, source, len);
   }
   else if (source && position == mSize)
   {
      append(source);
   }

   return(*this);
}

UtlString& UtlString::insert(size_t position, const char* source, size_t sourceLength)
{
   if(position <= mSize)
   {
      if(mCapacity < mSize + sourceLength + 1)
      {
         capacity(mSize + sourceLength + 1);
      }

      if (mpData)
      {
         memmove(&mpData[position + sourceLength],
                 &mpData[position],
                  mSize - position);

         memcpy(&mpData[position],
                 source,
                 sourceLength);

         mSize+= sourceLength;
         mpData[mSize] = '\0';
      }
   }

   // Else do nothing

   return(*this);
}

UtlString& UtlString::insert(size_t position, const char source)
{
   return(insert(position, &source, 1));
}

// Insert the designated string starting at character position start.
UtlString& UtlString::insert(size_t start, const UtlString& str)
{
    if(start >= 0 && start <= mSize && str.mpData && str.mSize > 0)
    {
        insert(start, str.mpData, str.mSize);
    }
    else
    {
        //do nothing.
    }

    return *this;
}


// Remove all characters after the specified position.
UtlString& UtlString::remove(size_t pos)
{
    if(mpData && pos >= 0 && pos < mSize)
    {
        mSize = pos;
        mpData[mSize] = '\0';
    }
    else
    {
        //do nothing
    }

    return *this;
}


// Remove N characters from this string starting at designated position.
UtlString& UtlString::remove(size_t pos, size_t N)
{
    if(mpData && N > 0 && N <= mSize - pos && pos >= 0 && pos < mSize)
    {
        // Add one extra byte for the '\0'
        size_t bytesToShift = mSize - (pos + N) + 1;

        // memcpy() cannot be used here due to possible overlap of source
        // and destination areas!
        memmove(&mpData[pos], &mpData[pos + N], bytesToShift);
        mSize -= N;
        mpData[mSize] = '\0';
    }
    else
    {
        //do nothing
    }

    return *this;
}


// Replace a single character at the designated position
void UtlString::replaceAt(size_t pos, char newChar)
{
   if (mpData && mSize > pos)
   {
      mpData[pos] = newChar;
   }
}


// Replace N characters starting at the designated position with the
// designated replacement string.
// use replace(size_t pos, size_t N, const char* replaceStr, size_t L).
UtlString& UtlString::replace(size_t pos, size_t N, const char* replaceStr)
{
    return replace(pos, N, replaceStr, strlen(replaceStr));
}

// Replace N characters starting at the designated position with the
// designated replacement string.
UtlString& UtlString::replace(size_t pos, size_t N, const UtlString& replaceStr)
{
    return replace(pos, N, replaceStr.mpData, replaceStr.mSize);
}

// Replace N characters starting at the designated position with a subset
// of the designated replacement string.
// use strncpy() to realize.
UtlString& UtlString::replace(size_t pos, size_t N, const char* replaceStr, size_t L)
{
    // FIX: need to avoid the allocation and extra copy if possible
    if ((replaceStr != NULL)      && 
        (pos >= 0)                && 
        (N >= 0)                  && 
        (strlen(replaceStr) >= L) &&
        (mpData)
       )
    {
        UtlString newUtlString;
        newUtlString.append(mpData, pos);
        newUtlString.append(replaceStr, L);
        if(int(mSize - N - pos) > 0)                //can not change to size_t
        {
            newUtlString.append(&mpData[pos + N], mSize - N - pos);
        }
        remove(0);
        append(newUtlString);
    }
    else
    {
        //do nothing;
    }

    return *this;
}


// Replace all instances of character src with character tgt
UtlString& UtlString::replace(const char src, const char tgt)
{
    if (mpData && (src!=0) && (tgt!=0))
    {
        for (size_t i=0; i<mSize; i++)
        {
            if (mpData[i] == src)
            {
                mpData[i] = tgt ;
            }
        }
    }
    return *this ;
}


// Removes whitespace (space, tab, Cr, Lf) from the end of the string.
// use strip(StripType type)
UtlString UtlString::strip()
{
    return strip(trailing);
}


// Removes whitespace (space, tab, Cr, Lf) from the beginning of the string,
// from the end of the string, or from both the beginning and end of the string.
// use strip(StripType , char ).
UtlString UtlString::strip(StripType type)
{
    if(mpData && mSize > 0)
    {
        if(type == both)
        {
            strip(leading);     //Here I use the method of recursion.
            strip(trailing);
        }
        else if(type == leading)
        {
            unsigned int removeLeading = 0;
            const char* leadingPtr = mpData;
            while(removeLeading < mSize &&
                  (*leadingPtr == '\t' ||
                   *leadingPtr == ' ' ||
                   *leadingPtr == '\n' ||
                   *leadingPtr == '\r'))
            {
                removeLeading++;
                leadingPtr++;
            }
            if(removeLeading > 0)
            {
                remove(0, removeLeading);
            }
        }
        else
        {
            unsigned int removeTrailing = 0;
            const char* trailingPtr = mpData + mSize - 1;
            while(removeTrailing < mSize &&
                  (*trailingPtr == '\t' ||
                   *trailingPtr == ' ' ||
                   *trailingPtr == '\n' ||
                   *trailingPtr == '\r'))
            {
                removeTrailing++;
                trailingPtr--;
            }
            if(removeTrailing > 0)
            {
                remove(mSize - removeTrailing);
            }
        }
    }

    return  *this;
}


// Remove the designated character from the beginning of the string,
// from the end of the string, or from both the beginning and end of
// the string.
// use remove(size_t pos, size_t N)
UtlString UtlString::strip(StripType type , char toBeStriped)
{
   if(mpData && mSize > 0)
   {
        if (type == leading || type == both)
        {
            unsigned int removeLeading = 0;
            while(mSize > removeLeading &&
                  mpData[removeLeading] == toBeStriped)
            {
                removeLeading++;
            }

            if(removeLeading > 0)
            {
                remove(0, removeLeading);
            }
        }

        if(type == trailing || type == both)
        {
            unsigned int removeTrailing = 0;
            while(mSize > removeTrailing &&
                  mpData[mSize - 1 - removeTrailing] == toBeStriped)
            {
                removeTrailing++;
            }
            if(removeTrailing > 0)
            {
                remove(mSize - removeTrailing);
            }
        }
    }

    return *this;
}


// Convert the string to all lower case characters.
// use tolower(char*) of Standard C++ Library.
void UtlString::toLower()
{
    if(mpData)
    {
        char* charPtr;

        for(size_t i = 0; i < mSize; i++)
        {
            charPtr = &mpData[i];
            *charPtr = tolower(*charPtr);
        }
    }
}


// Convert the string to all upper case characters.
// use toUpper(char*) of Standard C++ Library
void UtlString::toUpper()
{
    if(mpData)
    {
        char* charPtr;

        for(size_t i = 0; i < mSize; i++) //waring point
        {
            charPtr = &mpData[i];
            *charPtr = toupper(*charPtr);
        }
    }
}


// Resize the string to the specified size.
// use capacity(size_t).
void UtlString::resize(size_t N, UtlBoolean clearTail)
{
    if(N > mSize)
    {
        if (mCapacity <= N)
        {
            capacity(N + 1);
        }

        if (clearTail)
        {
            if (mpData)
            {
                for (; mSize < N; mSize++)
                {
                    mpData[mSize] = '\0';
                }
                mpData[mSize] = '\0';
            }
        } 
        else
        {
            mSize = N;
        }
    }
    else
    {
        remove(N);
    }
}

// Set the string's storage capacity to the designated value.
size_t UtlString::capacity(size_t N)
{
#ifdef _VXWORKS
    size_t maxFreeBlockSize = 0;
#endif
    size_t newSize = 0;
    char* newData = 0;

    if(mCapacity < N && N > 0)
    {
        if(mCapacity + UTLSTRING_MIN_INCREMENT > N)
        {
            N = mCapacity + UTLSTRING_MIN_INCREMENT;
        }
#ifdef _VXWORKS
        if (N > CHECK_BLOCK_THRESHOLD)
        {
            maxFreeBlockSize = FindMaxFreeBlock();
            // We are not going over the largest block size then try to
            // allocate otherwise we will return NULL, and the size returned
            // will be zero!
            if (N < maxFreeBlockSize)
            {
                newData = new char[N];
            }
        }
        else
        {
            newData = new char[N];
        }
#else

        //for all other OS's lets assume it really returns NULL and not crashes
        newData = new char[N];
#endif

        if (newData)
        {
            if(mSize > 0 && mpData)
            {
                memcpy(newData, mpData, mSize);
            }
            else
            {
                newData[0] = '\0';
            }
            if(mpData && mpData != mBuiltIn)
            {
                delete[] mpData;
            }
            mpData = newData;
            newSize = mCapacity = N;
        }
        else
        {
            osPrintf("******** ERROR******* : UtlString::capacity failed (%"PRIuMAX"). Memory not allocated!\n", N);
#ifdef _VXWORKS
            osPrintf("******** ERROR******* : Largest block = %"PRIuMAX", requested size = %"PRIuMAX"\n",maxFreeBlockSize, N);
#endif
            newSize = 0;
        }
    }
    else
    {
        newSize = mCapacity;
    }

    return(newSize);
}


//Addition operator
UtlString operator+(const UtlString& str1, const UtlString& str2)
{
    UtlString NewS = str1;
    NewS.append(str2);

    return NewS;

}


//Addition operator
UtlString operator+(const UtlString& str, const char* szStr)
{
    UtlString NewS = str;
    NewS.append(szStr);

    return NewS;
}


//Addition operator
UtlString operator+(const char* szStr, const UtlString& str)
{
    UtlString newS(szStr);
    newS.append(str);

    return newS;
}

/* ============================ ACCESSORS ================================= */

// Returns the index of the first occurrence of the character searchChar in self.
// Returns "-1" if there is no such character or if there is an embedded
// null prior to finding c.
// use index().
size_t UtlString::first(char searchChar) const
{
    return index(searchChar) ;
}


// Find the first instance of the designated string or UTLSTRING_NOT_FOUND
// if not found.
size_t UtlString::first(const char * searchStr) const
{
    return index(searchStr);
}


// Return the string length.
size_t UtlString::length() const
{
    return mSize;
}


// Return a read-only pointer to the underlying data.
const char* UtlString::data() const
{
    return(mpData ? mpData : ssNull);
}


// Return the index of the designated character or UTLSTRING_NOT_FOUND
// if not found.
size_t UtlString::index(char searchChar) const
{
    return index(searchChar, 0);
}


// Return the index of the designated character starting at the
// designated postion or UTLSTRING_NOT_FOUND if not found.
size_t UtlString::index(char searchChar, size_t start) const
{
    size_t foundPosition = UTLSTRING_NOT_FOUND;
    if(mpData)
    {
        size_t startIndex = start;

        for(size_t pos = startIndex;
            pos < mSize && foundPosition == UTLSTRING_NOT_FOUND;
            pos++)
        {
            if(mpData[pos] == searchChar)
            {
                 foundPosition = pos;
            }
        }
    }

    return foundPosition;
}

// Optimization to avoid strlen call as well as the only
// safe way to work with binary or opaque data
size_t UtlString::index(const UtlString& searchString) const
{
    return(index(searchString, 0));
}

// Optimization to avoid strlen call as well as the only
// safe way to work with binary or opaque data
size_t UtlString::index(const UtlString& searchString, size_t start) const
{
    size_t foundPosition = UTLSTRING_NOT_FOUND;
    // mpData may be null, so use data() which returns an
    // static empty string if mpData is null
    const char* dataPtr = data();
    size_t searchStrSize = searchString.length();
    size_t startIndex = start;

    if(searchStrSize <= mSize)
    {
        for (size_t pos = startIndex;
             pos <= (mSize - searchStrSize)
             && foundPosition == UTLSTRING_NOT_FOUND;
             pos++)
        {
            if (memcmp(dataPtr + pos, searchString.data(), searchStrSize) == 0)
            {
                foundPosition = pos;
            }
        }
    }

    return foundPosition;
}

// Return the index of the designated substring starting at the
// designated position or UTLSTRING_NOT_FOUND  if not found.
// Optimization to avoid strlen call as well as the only
// safe way to work with binary or opaque data
size_t UtlString::index(const UtlString& searchString, size_t start, CompareCase type) const
{
    size_t foundPosition = UTLSTRING_NOT_FOUND;
    size_t searchStrSize = searchString.length();
    size_t startIndex = start;

    if (type == matchCase)
    {

        foundPosition = index(searchString, start);
    }
    else
    {
        // mpData may be null, so use data() which returns an
        // static empty string if mpData is null
        const char* dataPtr = data();

        if (!dataPtr)
            assert(0);

        if(searchStrSize <= mSize)
        {
            for (size_t pos = startIndex;
                 pos <= (mSize - searchStrSize)
                 && foundPosition == UTLSTRING_NOT_FOUND;
                 pos++)
            {
                if(strncasecmp(dataPtr + pos,
                               searchString.data(),
                               searchStrSize) == 0)
                {
                    foundPosition = pos;
                }
            }
        }
    }

    return foundPosition;
}

// Return the index of the designated substring or UTLSTRING_NOT_FOUND if not found.
// Pattern matching. I think the arithmetic should be optimized.
// such as KMP: http://www.ics.uci.edu/~eppstein/161/960227.html
size_t UtlString::index(const char* searchStr) const
{
    return index(searchStr, 0);
}


// Return the index of the designated substring starting at the
// designated position or UTLSTRING_NOT_FOUND if not found.
size_t UtlString::index(const char* searchStr, size_t start) const
{
    size_t foundPosition = UTLSTRING_NOT_FOUND;
    if(searchStr)
    {
        // mpData may be null, so use data() which returns an
        // static empty string if mpData is null
        const char* dataPtr = data();

        // dataPtr better not be NULL
        if (!dataPtr)
            assert(0);

        size_t searchStrSize = strlen(searchStr);
        size_t startIndex = start;

        if(searchStrSize <= mSize)
        {
            for (size_t pos = startIndex;
                 pos <= (mSize - searchStrSize)
                 && foundPosition == UTLSTRING_NOT_FOUND;
                 pos++)
            {
                if (memcmp(dataPtr + pos, searchStr, searchStrSize) == 0)
                {
                    foundPosition = pos;
                }
            }
        }
    }

    return foundPosition;
}


// Return the index of the designated substring starting at the
// designated position or UTLSTRING_NOT_FOUND  if not found.
size_t UtlString::index(const char* searchStr, size_t start, CompareCase type) const
{
    size_t foundPosition = UTLSTRING_NOT_FOUND;
    if(searchStr)
    {
        size_t searchStrSize = strlen(searchStr);
        size_t startIndex = start;

        if (type == matchCase)
        {

            foundPosition = index(searchStr, start);
        }
        else
        {
            // mpData may be null, so use data() which returns an
            // static empty string if mpData is null
            const char* dataPtr = data();

            if (!dataPtr)
                assert(0);

            for (int pos = startIndex;
                 pos <= int(mSize - searchStrSize)
                 && foundPosition == UTLSTRING_NOT_FOUND;
                 pos++)
            {
                if(strncasecmp(dataPtr + pos, searchStr, searchStrSize) == 0)
                {
                    foundPosition = pos;
                }
            }

        }
    }

    return foundPosition;
}


// Returns the index of the last occurrence in the string of the character s.
size_t UtlString::last(char searchChar) const
{
    size_t foundPosition = UTLSTRING_NOT_FOUND;
    if(mpData)
    {
        size_t startIndex = 0;

        for(size_t pos = startIndex; pos < mSize; pos++)
        {
            if(mpData[pos] == searchChar)
            {
                foundPosition = pos;
            }
        }
    }

    return foundPosition;
}


// Return the storage capacity allocated for this string.
size_t UtlString::capacity() const
{
    return mCapacity;
}


// Returns an UtlString of self with length len, starting at index.
// Just return a copy.
UtlString UtlString::operator() (size_t start, size_t len) const
{
   UtlString newUtlString;

   // Note that since unsigned arithmetic can overflow, we have to test that
   // start + len >= start.
   if (mpData && start <= mSize && len == UTLSTRING_TO_END )
   {
      // Get all of the string after start.
      newUtlString.append(&mpData[start], mSize - start);
   } else if (mpData && start <= start + len && start + len <= mSize)
   {
      // Get len characters.
      newUtlString.append(&mpData[start], len);
   }

   return newUtlString;
}


#if 0
UtlString UtlString::operator()(const RegEx& re) const
{

}
#endif /* 0 */


// Cast this object to a const char* pointer.
UtlString::operator const char*() const
{
    return (data());
}


// Returns a hash value.
unsigned UtlString::hash() const
{
    // Need to use data() in case mpData is null
    const char* pHashData = data();
    size_t hashSize = mSize;
    unsigned hashValue = 0;

    while (hashSize > 0)
    {
        hashValue = (hashValue << 5) - hashValue + *pHashData;
        pHashData++;
        hashSize--;
    }

    return hashValue;
}


// Get the ContainableType for a UtlContainable derived class.
UtlContainableType UtlString::getContainableType() const
{
    return UtlString::TYPE;
}

/* ============================ INQUIRY =================================== */

// Not equals operator.
// use compareTo().
UtlBoolean operator!=(const char* compareStr, const UtlString& compareUtlStr)
{
    return compareUtlStr.compareTo(compareStr) != 0;
}


// Not equals operator.
// compare the first byte of str and the char.
UtlBoolean operator!=(const char compareChar, const UtlString& compareUtlStr)
{
    UtlBoolean operatorFlag = TRUE;

    if (compareUtlStr.mpData &&
        compareUtlStr.mSize == 1 &&
        compareUtlStr.mpData[0] == compareChar)
    {
        operatorFlag = FALSE;
    }

    return operatorFlag;
}


// Equals operator.
// compare the first byte of str and the char.
UtlBoolean operator==(const char compareChar, const UtlString& compareUtlStr)
{
    UtlBoolean operatorFlag = FALSE;

    if (compareUtlStr.mpData &&
        compareUtlStr.mSize == 1 &&
        compareUtlStr.mpData[0] == compareChar)
    {
        operatorFlag = TRUE;
    }

    return operatorFlag;
}


UtlBoolean operator==(const char* compareStr, const UtlString& compareUtlStr)
{
    return compareUtlStr.compareTo(compareStr) == 0;
}

UtlBoolean UtlString::operator==(const char *compareStr) const
{
    return compareTo(compareStr) == 0;
}

UtlBoolean UtlString::operator==(const UtlString& compareStr) const
{
    return compareTo(compareStr) == 0;
}

UtlBoolean UtlString::operator!=(const char *compareStr) const
{
    return compareTo(compareStr) != 0;
}

UtlBoolean UtlString::operator!=(const UtlString& compareStr) const
{
    return compareTo(compareStr) != 0;
}



// Compare this object to another like-objects.
int UtlString::compareTo(UtlContainable const * compareContainable) const
{
    int compareFlag = -1;

    if (compareContainable
                && compareContainable->isInstanceOf(UtlString::TYPE) == TRUE
           )
    {
        compareFlag = compareTo(((UtlString*) compareContainable)->data());
    }

    return compareFlag;
}


// Test this object to another like-object for equality.
UtlBoolean UtlString::isEqual(UtlContainable const * compareContainable) const
{
    return (compareTo(compareContainable) == 0);
}


// Compare this object to the specified string (case sensitive).
int UtlString::compareTo(const char * compareStr) const
{
    int compareFlag;

    if (compareStr)
    {
        compareFlag = compareTo(compareStr, matchCase);
    }
    else
    {
       // compareStr == NULL means it represents the null string.
       compareFlag = mSize > 0 ? 1 : 0;
    }

    return(compareFlag);
}


// Compare this object to the specified string with option of forcing
// either a case insensitive compare or a case sensitive compare.
int UtlString::compareTo(UtlString const * compareStr, CompareCase type) const
{
    int compareFlag = -1;

    if(compareStr->isInstanceOf(UtlString::TYPE) == TRUE)
    {
        if (mSize == 0 || compareStr->mSize == 0 )
        {
            if (compareStr->mSize != mSize)
            {
                compareFlag = mSize > compareStr->mSize ? 1 : -1;
            }
            else
            {
                compareFlag = 0;
            }
        }
        else
        {
            if (type == matchCase)
            {
                // BUG: Need to use memcmp mpData may have null char before end
                compareFlag = strcmp(mpData ? mpData : "", compareStr->mpData);
            }
            else
            {
                compareFlag = strcasecmp(mpData ? mpData : "", compareStr->mpData);
            }
        }
    }

    return compareFlag;
}


// Compare this object to the specified string with option of forcing
// either a case insensitive compare of a case sensitive compare.
int UtlString::compareTo(const char* compareStr, CompareCase type) const
{
    int compareFlag;

    if (type == matchCase)
    {
        // BUG: should use memcmp as compareStr may have null char before end
        compareFlag = strcmp(mpData ? mpData : "",
                             compareStr ? compareStr : "");
    }
    else
    {
       compareFlag = strcasecmp(mpData ? mpData : "",
                                compareStr ? compareStr : "");
    }

    return compareFlag;
}


// Returns TRUE if szStr occurs in self.
// use index(const char * szStr).
UtlBoolean UtlString::contains(const char* searchStr) const
{
    UtlBoolean containFlag = TRUE;

    size_t containPosition = index(searchStr);
    if (containPosition == UTLSTRING_NOT_FOUND)
    {
        containFlag = FALSE;
    }

    return containFlag;
}


// Return true if this is a empty (or null) string.
UtlBoolean UtlString::isNull() const
{
    return(mSize == 0);
}

UtlBoolean UtlString::isInstanceOf(const UtlContainableType type) const
{
    // Check if it is my type and the defer parent type comparisons to parent
    return(areSameTypes(type, UtlString::TYPE) ||
           UtlCopyableContainable::isInstanceOf(type));
}

/* //////////////////////////// PROTECTED ///////////////////////////////// */

/* //////////////////////////// PRIVATE /////////////////////////////////// */

#if defined(_VXWORKS)
/*
 *  perform string compare not caring for case of the
 *  characters. Compare only first N characters of the
 *  string.
 */

int strncasecmp( const char *s1, const char *s2, int N )
{
    int i;
    char c1, c2;


    // check simple case
    if ((s1 == s2) || (0 == N))
    {
        // match found 
        return 0; 
    }

    // if just one of them is NULL then there is a mistmatch
    if (!s1)
    {
       // NO match
       return -1;
    }
    else if (!s2)
    {
       // NO match
       return 1;
    }

    // loop thru all N entries
    for (i=0; i<N; i++, s1++, s2++)
    {
       c1 = *s1;
       c2 = *s2;

       // if either char is NULL, then break the test
       if (!c1 || !c2)
       {
          // match of BOTH are NULL
          break;
       }
          
       // quick compare without converting 
       if (c1 == c2)
       {
          continue;
       }

       // Now convert to lower case
       c1 = tolower(c1);
       c2 = tolower(c2);

       if (c1 == c2)
       {
          // same chars non-case sensitive 
          continue;
       }
       else
       {
          // mismatch
          break;
       }
    }

    // all characters same, match found
    return( c1 - c2 );
}

#endif  // #if defined(_VXWORKS)


/* ============================ INLINE METHODS ============================ */

