StringExt.h

// StringExt.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2004

#pragma once
#ifndef Ceda_cxUtils_StringExt_H
#define Ceda_cxUtils_StringExt_H

#include "cxUtils.h"
#include "xvector.h"
#include "xstring.h"
#include "xostream.h"
#include "StringStream.h"
#include "SubString.h"
#include "Hex.h"

namespace ceda
{

cxUtils_API void WriteIndent(xostream& os, ssize_t indent);


inline bool IsWhiteSpace(xchar c)
{
    return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\f';
}

inline bool IsWhiteSpaceWithoutLinefeed(xchar c)
{
    return c == ' ' || c == '\r' || c == '\t' || c == '\v' || c == '\f';
}

/*
inline bool IsWhiteSpaceWithoutLinefeedOrCarriageReturn(xchar c)
{
    return c == ' ' || c == '\t' || c == '\v' || c == '\f';
}
*/

inline int MapDecDigit(xchar c)
{
    if ('0' <= c && c <= '9') return c - '0';
    return -1;
}


inline void DeleteChar(xstring& s, ssize_t index)
{
    s.erase(s.begin() + index);
}

inline void DeleteChars(xstring& s, ssize_t i1, ssize_t i2)
{
    s.erase(s.begin() + i1, s.begin() + i2);
}

// Returns the index position of the first character c in s or -1 if not found
cxUtils_API ssize_t StringFind(const xstring& s, xchar c);

// Returns the index position of the last character c in s or -1 if not found
cxUtils_API ssize_t StringReverseFind(const xstring& s, xchar c);

// Split string s into pieces using the given delimiter characters.  The pieces are pushed into
// the vector list.
cxUtils_API void SplitStringUsingDelimiters(const xstring& s, xvector<xstring>& list, const xstring& delimiters);

// Remove all characters from s starting from the first occurence of the given delimiter character
cxUtils_API void TruncateStringPastDelimiter(xstring& s, xchar delimiter);

// Copy src string to dst string, removing any characters that appear in 'charsToRemove' 
cxUtils_API void RemoveCharsFromString(const xstring& src, xstring* dst, const xstring& charsToRemove);

// Remove any characters in s that appear in 'charsToRemove' 
inline void RemoveCharsFromString(xstring& s, const xstring& charsToRemove)
{
    xstring src = s;
    RemoveCharsFromString(src, &s, charsToRemove);
}


// Write src string to dst string changing every instance of character cfind with creplace, 
cxUtils_API void FindAndReplaceCharInString(const xstring& src, xstring* dst, xchar cfind, xchar creplace);

// Replace occurrences of string sfind found in src with sreplace
cxUtils_API void StringFindAndReplace(xstring& src, const xstring& sfind, const xstring& sreplace);

// Changing every instance of character cfind with creplace in s
inline void FindAndReplaceCharInString(xstring& s, xchar cfind, xchar creplace)
{
    xstring src = s;
    FindAndReplaceCharInString(src,&s,cfind,creplace);
}

// Write src string to dst string, replacing each tab character with the given number of spaces
cxUtils_API void ReplaceTabsWithSpacesInString(const xstring& src, xstring* dst, ssize_t tabSize);

// Returns true if s has some repeated characters
// This implementation actually tests for repeated bytes, so can't be expected to work properly
// with general UTF-8 strings
cxUtils_API bool AsciiStringHasRepeatedChars(const xstring& s);

// Returns true if string s only contains characters drawn from string t
// This implementation only works for ASCII characters in t
cxUtils_API bool AsciiStringOnlyContainsGivenChars(const xstring& s, const xstring& t);

// Returns true if string s contains any characters drawn from string t
// This implementation only works for ASCII characters in t
cxUtils_API bool AsciiStringContainsAnyGivenChars(const xstring& s, const xstring& t);

// Eat characters at the start or end of string s that are drawn from string t
cxUtils_API void EatFirstCharsInString(xstring& s, const xstring& t);
cxUtils_API void EatLastCharsInString(xstring& s, const xstring& t);

// If string s contains spaces then returns double quoted version of s else merely returns a 
// copy of s
cxUtils_API xstring AddQuotesToStringIfHaveSpaces(const xstring& s);

// Eat all spaces, tabs, line feeds and carriage returns
cxUtils_API void EatWhiteSpaceInString(xstring& s);

// Perform a basic lexical scan on string s, breaking it up according to the white space
cxUtils_API void BasicLexicalScan(const xstring& s, xvector<xstring>& list);

// Format string s using printf style syntax
cxUtils_API void FormatString(xstring& s, ConstStringZ format, ...);

// Add the given number of spaces to the front of every line in s
cxUtils_API void StringIndent(xstring& s, ssize_t numSpaces);

// Convert any upper case characters to lower case characters
cxUtils_API void StringToLower(xstring& s);

// Convert any lower case characters to upper case characters
cxUtils_API void StringToUpper(xstring& s);

// Read string s from a text file.  Returns false if file not found
cxUtils_API bool ReadStringFromFile(xstring& s, const xstring& filename, bool textmode = true);

// Write string to a text file.  Returns false if could not write the file
cxUtils_API bool WriteStringToFile(const xstring& s, const xstring& filename, bool textmode = true);

// Conversions between Unicode and ANSI
cxUtils_API void StrcpyANSItoUnicode(char16* dst, const char8* src);
cxUtils_API void StrcpyUnicodeToANSI(char8* dst, const char16* src);

// Append the range [s1,s2) to the given destination string
cxUtils_API void StringAppend(xstring& dst, const xchar* s1, const xchar* s2);

cxUtils_API void SearchAndReplace(const xstring& src, xstring& dst, const xstring& findStr, const xstring& repStr);
cxUtils_API void SearchAndReplace(const xstring& src, xostream& os, const xstring& findStr, const xstring& repStr);

cxUtils_API bool IsEqualNoCase(const xstring& s1, const xstring& s2);

///////////////////////////////////////////////////////////////////////////////////////////////////
// StringToNumber

template <typename T> bool StringToNumber(const xstring& str, T& value, xstring& reasonInvalid, int base = 10);

#define cxDefine_StringToInteger(T,Name) \
    cxUtils_API bool StringTo ## Name(const xstring& str, T& value, xstring& reasonInvalid, int base = 10); \
    template <> inline bool StringToNumber(const xstring& str, T& value, xstring& reasonInvalid, int base) \
        { return StringTo ## Name(str, value, reasonInvalid, base); }

cxDefine_StringToInteger(int8,Int8)
cxDefine_StringToInteger(uint8,UInt8)

cxDefine_StringToInteger(int16,Int16)
cxDefine_StringToInteger(uint16,UInt16)

cxDefine_StringToInteger(int32,Int32)
cxDefine_StringToInteger(uint32,UInt32)

cxDefine_StringToInteger(int64,Int64)
cxDefine_StringToInteger(uint64,UInt64)

cxUtils_API bool StringToFloat(const xstring& str, float& value, xstring& reasonInvalid);
cxUtils_API bool StringToDouble(const xstring& str, double& value, xstring& reasonInvalid);

template <> inline bool StringToNumber(const xstring& str, float& value, xstring& reasonInvalid, int base)
{
    cxAssert(base == 10);
    return StringToFloat(str, value, reasonInvalid);
}

template <> inline bool StringToNumber(const xstring& str, double& value, xstring& reasonInvalid, int base)
{
    cxAssert(base == 10);
    return StringToDouble(str, value, reasonInvalid);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// NumberToString

template <typename T> inline xstring NumberToString( const T& value )
{
    StringStream os;
    os << value;
    return os.GetString();
}

/*
It is necessary to deal with 8 bit integers specially because unfortunately 'char' isn't a 
distinct type
*/

template <> inline xstring NumberToString( const int8& value )
{
    StringStream os;
    os << (int) value;
    return os.GetString();
}

template <> inline xstring NumberToString( const uint8& value )
{
    StringStream os;
    os << (int) value;
    return os.GetString();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Convenient wrapper for writing a size in kilobytes / megabytes / gigabytes / terrabytes

cxUtils_API void WriteByteSizeNumber(xostream& os, double x);

template <typename T>
struct ByteSizeNumber
{
    ByteSizeNumber(T value) : m_value(value) {}
    T m_value;
};

template <typename T>
ByteSizeNumber<T> AsByteSize(T value) { return ByteSizeNumber<T>(value); }

template <typename T>
xostream& operator<<(xostream& os, ByteSizeNumber<T> bsn)
{
    WriteByteSizeNumber(os,static_cast<double>(bsn.m_value));
    return os;
}

template<typename T>
void SubStringToHexInt(SubString str, T& value, bool expect0x = true)
{
    ssize_t startPos;
    ssize_t n = str.size();

    if (expect0x)
    {
        cxAssert(n > 2);
        cxAssert(str[0] == '0');
        cxAssert(str[1] == 'x' || str[1] == 'X');
        startPos = 2;
    }
    else
    {
        startPos = 0;
    }
    
    value = 0;
    for (ssize_t i=startPos ; i < n ; ++i)
    {
        int d = MapHexDigit(str[i]);
        cxAssert(d != -1);
        value = (T) ((value << 4) | d);
    }
}

template<typename T>
void SubStringToInt(SubString str, T& value)
{
    ssize_t n = str.size();
    cxAssert(n > 0);
    value = 0;
    for (ssize_t i=0 ; i < n ; ++i)
    {
        int d = MapDecDigit(str[i]);
        cxAssert(d != -1);
        value = value * 10 + d;
    }
}

template <typename T>
bool SubStringToHex(SubString& s, T& x)
{
    const xchar* start = s.begin();
    x = 0;
    int d;
    while(s && (d = MapHexDigit(*s)) != -1)
    {
        x = (x << 4) | d;
        ++s;
    }
    return s.begin() != start;     // Return true if read one or more digits
}

} // namespace ceda

#endif // include guard