genesis-3d_engine/Engine/foundation/util/string.cc

1298 lines
33 KiB
C++
Raw Normal View History

/****************************************************************************
Copyright (c) 2006, Radon Labs GmbH
Copyright (c) 2011-2013,WebJet Business Division,CYOU
http://www.genesis-3d.com.cn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "stdneb.h"
#include "exception/exceptions.h"
#include "util/string.h"
#include "util/assetpath.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
namespace Util
{
//------------------------------------------------------------------------------
/**
*/
bool
String::CopyToBuffer(char* buf, SizeT bufSize) const
{
n_assert(0 != buf);
n_assert(bufSize > 1);
#if __WIN32__
HRESULT hr = StringCchCopy(buf, bufSize, this->AsCharPtr());
return SUCCEEDED(hr);
#else
strncpy(buf, this->AsCharPtr(), bufSize - 1);
buf[bufSize - 1] = 0;
return this->strLen < bufSize;
#endif
}
//------------------------------------------------------------------------------
/**
*/
void __cdecl
String::Format(const char* fmtString, ...)
{
va_list argList;
va_start(argList, fmtString);
char buf[4096]; // an 4 kByte buffer
#if __WIN32__
// need to use non-CRT thread safe function under Win32
StringCchVPrintf(buf, sizeof(buf), fmtString, argList);
#elif (__WII__ || __PS3__ || __OSX__ || __ANDROID__)
vsnprintf(buf, sizeof(buf), fmtString, argList);
#else
_vsnprintf(buf, sizeof(buf), fmtString, argList);
#endif
*this = buf;
va_end(argList);
}
//------------------------------------------------------------------------------
/**
*/
const String&
String::Format2(const char* fmtString, ...)
{
va_list argList;
va_start(argList, fmtString);
FormatArgList(fmtString, argList);
va_end(argList);
return *this;
}
//------------------------------------------------------------------------------
/**
*/
void __cdecl
String::FormatArgList(const char* fmtString, va_list argList)
{
char buf[4096]; // an 4 kByte buffer
#if __WIN32__
// need to use non-CRT thread safe function under Win32
StringCchVPrintf(buf, sizeof(buf), fmtString, argList);
#elif (__WII__ || __PS3__ || __OSX__ || __ANDROID__)
vsnprintf(buf, sizeof(buf), fmtString, argList);
#else
_vsnprintf(buf, sizeof(buf), fmtString, argList);
#endif
*this = buf;
}
//------------------------------------------------------------------------------
/**
Sets a new string content. This will handle all special cases and try
to minimize heap allocations as much as possible.
*/
void
String::Set(const char* str, SizeT length)
{
if (0 == str)
{
// a null string pointer is valid as a special case
this->Delete();
this->localBuffer[0] = 0;
}
else if ((0 == this->heapBuffer) && (length < LocalStringSize))
{
// the new contents fits into the local buffer,
// however, if there is already an external buffer
// allocated, we use the external buffer!
this->Delete();
Memory::Copy(str, this->localBuffer, length);
this->localBuffer[length] = 0;
}
else if (length < this->heapBufferSize)
{
// the new contents fits into the existing buffer
Memory::Copy(str, this->heapBuffer, length);
this->heapBuffer[length] = 0;
this->localBuffer[0] = 0;
}
else
{
// need to allocate bigger heap buffer
this->Alloc(length + 1);
Memory::Copy(str, this->heapBuffer, length);
this->heapBuffer[length] = 0;
this->localBuffer[0] = 0;
}
this->strLen = length;
}
//------------------------------------------------------------------------------
/**
*/
void
String::AppendRange(const char* append, SizeT length)
{
n_assert(append);
if (length > 0)
{
SizeT newLength = this->strLen + length;
// if there is an external buffer allocated, we need to stay there
// even if the resulting string is smaller then LOCALSTRINGSIZE,
// a small string in an external buffer may be the result
// of a call to Reserve()
if ((0 == this->heapBuffer) && (newLength < LocalStringSize))
{
// the result fits into the local buffer
Memory::Copy(append, this->localBuffer + this->strLen, length);
this->localBuffer[newLength] = 0;
}
else if (newLength < this->heapBufferSize)
{
// the result fits into the existing buffer
Memory::Copy(append, this->heapBuffer + this->strLen, length);
this->heapBuffer[newLength] = 0;
}
else
{
// need to re-allocate
this->Realloc(newLength + newLength / 2);
Memory::Copy(append, this->heapBuffer + this->strLen, length);
this->heapBuffer[newLength] = 0;
}
this->strLen = newLength;
}
}
//------------------------------------------------------------------------------
/**
Tokenize the string into a String array.
@param whiteSpace a string containing the whitespace characters
@return a string array of tokens
*/
SizeT
String::Tokenize(const String& whiteSpace, Array<String>& outTokens) const
{
outTokens.Clear();
String str(*this);
char* ptr = const_cast<char*>(str.AsCharPtr());
const char* token;
while (0 != (token = strtok(ptr, whiteSpace.AsCharPtr())))
{
outTokens.Append(token);
ptr = 0;
}
return outTokens.Size();
}
//------------------------------------------------------------------------------
/**
This is the slow-but-convenient Tokenize() method. Slow since the
returned string array will be constructed anew with every method call.
Consider the Tokenize() method which takes a string array as input,
since this may allow reusing of the array, reducing heap allocations.
*/
Array<String>
String::Tokenize(const String& whiteSpace) const
{
Array<String> tokens;
this->Tokenize(whiteSpace, tokens);
return tokens;
}
//------------------------------------------------------------------------------
/**
Tokenize a string, but keeps the string within the fence-character
intact. For instance for the sentence:
He said: "I don't know."
A Tokenize(" ", '"', tokens) would return:
token 0: He
token 1: said:
token 2: I don't know.
*/
SizeT
String::Tokenize(const String& whiteSpace, char fence, Array<String>& outTokens) const
{
outTokens.Clear();
String str(*this);
char* ptr = const_cast<char*>(str.AsCharPtr());
char* end = ptr + strlen(ptr);
while (ptr < end)
{
char* c;
// skip white space
while (*ptr && strchr(whiteSpace.AsCharPtr(), *ptr))
{
ptr++;
}
if (*ptr)
{
// check for fenced area
if ((fence == *ptr) && (0 != (c = strchr(++ptr, fence))))
{
*c++ = 0;
outTokens.Append(ptr);
ptr = c;
}
else if (0 != (c = strpbrk(ptr, whiteSpace.AsCharPtr())))
{
*c++ = 0;
outTokens.Append(ptr);
ptr = c;
}
else
{
outTokens.Append(ptr);
break;
}
}
}
return outTokens.Size();
}
//------------------------------------------------------------------------------
/**
Slow version of Tokenize() with fence character. See above Tokenize()
for details.
*/
Array<String>
String::Tokenize(const String& whiteSpace, char fence) const
{
Array<String> tokens;
this->Tokenize(whiteSpace, fence, tokens);
return tokens;
}
//------------------------------------------------------------------------------
/**
Extract a substring range.
*/
String
String::ExtractRange(IndexT from, SizeT numChars) const
{
n_assert(from <= this->strLen);
n_assert((from + numChars) <= this->strLen);
const char* str = this->AsCharPtr();
String newString;
newString.Set(&(str[from]), numChars);
return newString;
}
//------------------------------------------------------------------------------
/**
Extract a substring until the end of the original string.
*/
String
String::ExtractToEnd(IndexT fromIndex) const
{
return this->ExtractRange(fromIndex, this->strLen - fromIndex);
}
//------------------------------------------------------------------------------
/**
Erase substring of this string.
*/
String
String::EraseRange(IndexT fromIndex, SizeT numChars) const
{
n_assert(numChars >= 0);
n_assert(fromIndex <= this->strLen);
SizeT toIndex = numChars + fromIndex;
n_assert(toIndex <= this->strLen);
String newString, endString;
endString = this->ExtractToEnd(toIndex);
newString.Set(this->AsCharPtr(), fromIndex);
newString.Append(endString);
return newString;
}
String
String::InsertRange(IndexT fromIndex,const String& str) const
{
n_assert(fromIndex <= this->strLen);
const char* startStr = this->AsCharPtr();
String newString,endString;
endString = this->ExtractToEnd(fromIndex);
newString.Set(this->AsCharPtr(), fromIndex);
newString.Append(str);
newString.Append(endString);
return newString;
}
//------------------------------------------------------------------------------
/**
Terminates the string at the first occurance of one of the characters
in charSet.
*/
void
String::Strip(const String& charSet)
{
char* str = const_cast<char*>(this->AsCharPtr());
char* ptr = strpbrk(str, charSet.AsCharPtr());
if (ptr)
{
*ptr = 0;
}
this->strLen = (SizeT) strlen(this->AsCharPtr());
}
//------------------------------------------------------------------------------
/**
Return the index of a substring, or InvalidIndex if not found.
*/
IndexT
String::FindStringIndex(const String& s, IndexT startIndex) const
{
n_assert(startIndex < this->strLen);
n_assert(s.IsValid());
IndexT i;
for (i = startIndex; i < this->strLen; i++)
{
if ((this->strLen - i) < s.strLen)
{
break;
}
if (strncmp(&(this->AsCharPtr()[i]), s.AsCharPtr(), s.strLen) == 0)
{
return i;
}
}
return InvalidIndex;
}
//------------------------------------------------------------------------------
/**
Return index of character in string, or InvalidIndex if not found.
*/
IndexT
String::FindCharIndex(char c, IndexT startIndex) const
{
if (this->strLen > 0)
{
n_assert(startIndex < this->strLen);
const char* ptr = strchr(this->AsCharPtr() + startIndex, c);
if (ptr)
{
return IndexT(ptr - this->AsCharPtr());
}
}
return InvalidIndex;
}
/**
search a char in a backward direction for the first occurrence,
return the index(forward direction) if success.
note:this function will not treat '\0' as terminal of the string
*/
IndexT
String::BackwardFindChar(char c, IndexT startIndex/* = 0*/ )const
{
if ( this->strLen>0 )
{
n_assert( startIndex<this->strLen );
SizeT idx = this->strLen - startIndex - 1;// - index starts form 0
const char* ptr = this->AsCharPtr() + idx;
while ( idx>=0 &&
*ptr!=c )
{
ptr--;
idx--;
}
if ( *ptr==c )
{
return idx;
}
}
return InvalidIndex;
}
//------------------------------------------------------------------------------
/**
Removes all characters in charSet from the left side of the string.
*/
void
String::TrimLeft(const String& charSet)
{
n_assert(charSet.IsValid());
if (this->IsValid())
{
SizeT charSetLen = charSet.strLen;
IndexT thisIndex = 0;
bool stopped = false;
while (!stopped && (thisIndex < this->strLen))
{
IndexT charSetIndex;
bool match = false;
for (charSetIndex = 0; charSetIndex < charSetLen; charSetIndex++)
{
if ((*this)[thisIndex] == charSet[charSetIndex])
{
// a match
match = true;
break;
}
}
if (!match)
{
// stop if no match
stopped = true;
}
else
{
// a match, advance to next character
++thisIndex;
}
}
String trimmedString(&(this->AsCharPtr()[thisIndex]));
*this = trimmedString;
}
}
//------------------------------------------------------------------------------
/**
Removes all characters in charSet from the right side of the string.
*/
void
String::TrimRight(const String& charSet)
{
n_assert(charSet.IsValid());
if (this->IsValid())
{
SizeT charSetLen = charSet.strLen;
int thisIndex = this->strLen - 1; // NOTE: may not be unsigned (thus not IndexT!)
bool stopped = false;
while (!stopped && (thisIndex >= 0))
{
IndexT charSetIndex;
bool match = false;
for (charSetIndex = 0; charSetIndex < charSetLen; charSetIndex++)
{
if ((*this)[thisIndex] == charSet[charSetIndex])
{
// a match
match = true;
break;
}
}
if (!match)
{
// stop if no match
stopped = true;
}
else
{
// a match, advance to next character
--thisIndex;
}
}
String trimmedString;
trimmedString.Set(this->AsCharPtr(), thisIndex + 1);
*this = trimmedString;
}
}
//------------------------------------------------------------------------------
/**
Trim both sides of a string.
*/
void
String::Trim(const String& charSet)
{
this->TrimLeft(charSet);
this->TrimRight(charSet);
}
//------------------------------------------------------------------------------
/**
Substitute every occurance of origStr with substStr.
*/
void
String::SubstituteString(const String& matchStr, const String& substStr)
{
n_assert(matchStr.IsValid());
const char* ptr = this->AsCharPtr();
SizeT matchStrLen = matchStr.strLen;
String dest;
// walk original string for occurances of str
const char* occur;
while (0 != (occur = strstr(ptr, matchStr.AsCharPtr())))
{
// append string fragment until match
dest.AppendRange(ptr, SizeT(occur - ptr));
// append replacement string
if (substStr.Length() > 0)
{
dest.Append(substStr);
}
// adjust source pointer
ptr = occur + matchStrLen;
}
dest.Append(ptr);
*this = dest;
}
//------------------------------------------------------------------------------
/**
Return a String object containing the last directory of the path, i.e.
a category.
- 17-Feb-04 floh fixed a bug when the path ended with a slash
*/
String
String::ExtractLastDirName() const
{
String pathString(*this);
char* lastSlash = pathString.GetLastSlash();
// special case if path ends with a slash
if (lastSlash)
{
if (0 == lastSlash[1])
{
*lastSlash = 0;
lastSlash = pathString.GetLastSlash();
}
char* secLastSlash = 0;
if (0 != lastSlash)
{
*lastSlash = 0; // cut filename
secLastSlash = pathString.GetLastSlash();
if (secLastSlash)
{
*secLastSlash = 0;
return String(secLastSlash+1);
}
}
}
return "";
}
//------------------------------------------------------------------------------
/**
Return a String object containing the part before the last
directory separator.
NOTE: I left my fix in that returns the last slash (or colon), this was
necessary to tell if a dirname is a normal directory or an assign.
- 17-Feb-04 floh fixed a bug when the path ended with a slash
*/
String
String::ExtractDirName() const
{
String pathString(*this);
char* lastSlash = pathString.GetLastSlash();
// special case if path ends with a slash
if (lastSlash)
{
if (0 == lastSlash[1])
{
*lastSlash = 0;
lastSlash = pathString.GetLastSlash();
}
if (lastSlash)
{
*++lastSlash = 0;
}
}
pathString.strLen = (SizeT) strlen(pathString.AsCharPtr());
return pathString;
}
//------------------------------------------------------------------------------
/**
Pattern-matching, TCL-style.
*/
bool
String::MatchPattern(const String& string, const String& pattern)
{
const char* str = string.AsCharPtr();
const char* pat = pattern.AsCharPtr();
char c2;
bool always = true;
while (always != false)
{
if (*pat == 0)
{
if (*str == 0) return true;
else return false;
}
if ((*str == 0) && (*pat != '*')) return false;
if (*pat=='*')
{
pat++;
if (*pat==0) return true;
while (always)
{
if (String::MatchPattern(str, pat)) return true;
if (*str==0) return false;
str++;
}
}
if (*pat=='?') goto match;
if (*pat=='[')
{
pat++;
while (always)
{
if ((*pat==']') || (*pat==0)) return false;
if (*pat==*str) break;
if (pat[1] == '-')
{
c2 = pat[2];
if (c2==0) return false;
if ((*pat<=*str) && (c2>=*str)) break;
if ((*pat>=*str) && (c2<=*str)) break;
pat+=2;
}
pat++;
}
while (*pat!=']')
{
if (*pat==0)
{
pat--;
break;
}
pat++;
}
goto match;
}
if (*pat=='\\')
{
pat++;
if (*pat==0) return false;
}
if (*pat!=*str) return false;
match:
pat++;
str++;
}
// can't happen
return false;
}
//------------------------------------------------------------------------------
/**
*/
void
String::ReplaceChars(const String& charSet, char replacement)
{
n_assert(charSet.IsValid());
char* ptr = const_cast<char*>(this->AsCharPtr());
char c;
while (0 != (c = *ptr))
{
if (strchr(charSet.AsCharPtr(), c))
{
*ptr = replacement;
}
ptr++;
}
}
//------------------------------------------------------------------------------
/**
Returns content as matrix44. Note: this method doesn't check whether the
contents is actually a valid matrix44. Use the IsValidMatrix44() method
for this!
*/
Math::matrix44
String::AsMatrix44() const
{
Array<String> tokens(16, 0);
this->Tokenize(", \t\n\r", tokens);
if(tokens.Size() != 16)
{
SYS_EXCEPT(Exceptions::FormatException,
STRING_FORMAT("invalid string value for matrix44: %s.", this->AsCharPtr()),
GET_FUNCTION_NAME()
);
}
Math::matrix44 m(Math::float4(tokens[0].AsFloat(), tokens[1].AsFloat(), tokens[2].AsFloat(), tokens[3].AsFloat()),
Math::float4(tokens[4].AsFloat(), tokens[5].AsFloat(), tokens[6].AsFloat(), tokens[7].AsFloat()),
Math::float4(tokens[8].AsFloat(), tokens[9].AsFloat(), tokens[10].AsFloat(), tokens[11].AsFloat()),
Math::float4(tokens[12].AsFloat(), tokens[13].AsFloat(), tokens[14].AsFloat(), tokens[15].AsFloat()));
return m;
}
//------------------------------------------------------------------------------
/**
*/
String
String::Concatenate(const Array<String>& strArray, const String& whiteSpace)
{
String res;
res.Reserve(256);
IndexT i;
SizeT num = strArray.Size();
for (i = 0; i < num; i++)
{
res.Append(strArray[i]);
if ((i + 1) < num)
{
res.Append(whiteSpace);
}
}
return res;
}
//------------------------------------------------------------------------------
/**
*/
void
String::SetCharPtr(const char* s)
{
SizeT len = 0;
if (s)
{
len = (SizeT) strlen((const char*)s);
}
this->Set(s, len);
}
//------------------------------------------------------------------------------
/**
*/
void
String::Append(const char* str)
{
n_assert(0 != str);
this->AppendRange(str, (SizeT) strlen(str));
}
//------------------------------------------------------------------------------
/**
Terminates the string at the given index.
*/
void
String::TerminateAtIndex(IndexT index)
{
n_assert(index < this->strLen);
char* ptr = const_cast<char*>(this->AsCharPtr());
ptr[index] = 0;
this->strLen = (SizeT) strlen(ptr);
}
//------------------------------------------------------------------------------
/**
Remove the file extension.
*/
void
String::StripFileExtension()
{
char* str = const_cast<char*>(this->AsCharPtr());
char* ext = strrchr(str, '.');
if (ext)
{
*ext = 0;
this->strLen = (SizeT) strlen(this->AsCharPtr());
}
}
//------------------------------------------------------------------------------
/**
*/
bool
operator==(const String& a, const String& b)
{
return strcmp(a.AsCharPtr(), b.AsCharPtr()) == 0;
}
//------------------------------------------------------------------------------
/**
*/
bool
operator==(const String& a, const char* cStr)
{
n_assert(0 != cStr);
return strcmp(a.AsCharPtr(), cStr) == 0;
}
//------------------------------------------------------------------------------
/**
*/
bool
operator==(const char* cStr, const String& b)
{
n_assert(0 != cStr);
return strcmp(cStr, b.AsCharPtr()) == 0;
}
//------------------------------------------------------------------------------
/**
*/
bool
operator != (const String& a, const String& b)
{
return strcmp(a.AsCharPtr(), b.AsCharPtr()) != 0;
}
//------------------------------------------------------------------------------
/**
*/
bool
operator < (const String& a, const String& b)
{
return strcmp(a.AsCharPtr(), b.AsCharPtr()) < 0;
}
//------------------------------------------------------------------------------
/**
*/
bool
operator > (const String& a, const String& b)
{
return strcmp(a.AsCharPtr(), b.AsCharPtr()) > 0;
}
//------------------------------------------------------------------------------
/**
*/
bool
operator <= (const String& a, const String& b)
{
return strcmp(a.AsCharPtr(), b.AsCharPtr()) <= 0;
}
//------------------------------------------------------------------------------
/**
*/
bool
operator >= (const String& a, const String& b)
{
return strcmp(a.AsCharPtr(), b.AsCharPtr()) >= 0;
}
//------------------------------------------------------------------------------
/**
*/
void
String::ToLower()
{
char* str = const_cast<char*>(this->AsCharPtr());
char c;
while (0 != (c = *str))
{
*str++ = (char) tolower(c);
}
}
//------------------------------------------------------------------------------
/**
*/
void
String::ToUpper()
{
char* str = const_cast<char*>(this->AsCharPtr());
char c;
while (0 != (c = *str))
{
*str++ = (char) toupper(c);
}
}
//------------------------------------------------------------------------------
/**
*/
void
String::FirstCharToUpper()
{
char* str = const_cast<char*>(this->AsCharPtr());
*str = (char) toupper(*str);
}
//------------------------------------------------------------------------------
/**
Returns true if string contains one of the characters from charset.
*/
bool
String::ContainsCharFromSet(const String& charSet) const
{
char* str = const_cast<char*>(this->AsCharPtr());
char* ptr = strpbrk(str, charSet.AsCharPtr());
return (0 != ptr);
}
//------------------------------------------------------------------------------
/**
@return string representing the filename extension (maybe empty)
*/
String
String::GetFileExtension() const
{
const char* str = this->AsCharPtr();
const char* ext = strrchr(str, '.');
if (ext)
{
ext++;
return String(ext);
}
return String("");
}
//------------------------------------------------------------------------------
/**
Get a pointer to the last directory separator.
*/
char*
String::GetLastSlash() const
{
const char* s = this->AsCharPtr();
const char* lastSlash = strrchr(s, '/');
if (0 == lastSlash) lastSlash = strrchr(s, '\\');
if (0 == lastSlash) lastSlash = strrchr(s, ':');
return const_cast<char*>(lastSlash);
}
//------------------------------------------------------------------------------
/**
*/
bool
String::IsValidBool() const
{
static const char* bools[] = {
"no", "yes", "off", "on", "false", "true", 0
};
IndexT i = 0;
while (bools[i] != 0)
{
if (0 == n_stricmp(bools[i], this->AsCharPtr()))
{
return true;
}
i++;
}
return false;
}
//------------------------------------------------------------------------------
/**
Returns content as integer. Note: this method doesn't check whether the
contents is actually a valid integer. Use the IsValidInteger() method
for this!
*/
int
String::AsInt() const
{
return atoi(this->AsCharPtr());
}
//------------------------------------------------------------------------------
/**
Returns content as float. Note: this method doesn't check whether the
contents is actually a valid float. Use the IsValidInt() method
for this!
*/
float
String::AsFloat() const
{
return float(atof(this->AsCharPtr()));
}
//------------------------------------------------------------------------------
/**
*/
bool
String::AsBool() const
{
static const char* bools[] = {
"no", "yes", "off", "on", "false", "true", 0
};
IndexT i = 0;
while (bools[i] != 0)
{
if (0 == n_stricmp(bools[i], this->AsCharPtr()))
{
return 1 == (i & 1);
}
i++;
}
//n_error("Invalid string value for bool!");
SYS_EXCEPT(Exceptions::FormatException,
STRING_FORMAT("invalid string value for bool: %s.", this->AsCharPtr()),
GET_FUNCTION_NAME()
);
return false;
}
//------------------------------------------------------------------------------
void String::SetAssetPath( const Util::AssetPath& v )
{
this->Format("%s,%d",
v.path.AsCharPtr(),v.type);
}
//------------------------------------------------------------------------------
/**
Returns content as float2. Note: this method doesn't check whether the
contents is actually a valid float4. Use the IsValidFloat2() method
for this!
*/
Math::float2
String::AsFloat2() const
{
Array<String> tokens(2, 0);
this->Tokenize(", \t", tokens);
if(tokens.Size() != 2)
{
SYS_EXCEPT(Exceptions::FormatException,
STRING_FORMAT("invalid string value for float2: %s.", this->AsCharPtr()),
GET_FUNCTION_NAME()
);
}
Math::float2 v(tokens[0].AsFloat(), tokens[1].AsFloat());
return v;
}
//------------------------------------------------------------------------------
/**
Returns content as float3. Note: this method doesn't check whether the
contents is actually a valid float3. Use the IsValidFloat3() method
for this!
*/
Math::float3
String::AsFloat3() const
{
Array<String> tokens(3, 0);
this->Tokenize(", \t", tokens);
if(tokens.Size() != 3)
{
SYS_EXCEPT(Exceptions::FormatException,
STRING_FORMAT("invalid string value for float3: %s.", this->AsCharPtr()),
GET_FUNCTION_NAME()
);
}
Math::float3 v(tokens[0].AsFloat(), tokens[1].AsFloat(), tokens[2].AsFloat());
return v;
}
//------------------------------------------------------------------------------
/**
Returns content as float4. Note: this method doesn't check whether the
contents is actually a valid float4. Use the IsValidFloat4() method
for this!
*/
Math::float4
String::AsFloat4() const
{
Array<String> tokens(4, 0);
this->Tokenize(", \t", tokens);
if(tokens.Size() != 4)
{
SYS_EXCEPT(Exceptions::FormatException,
STRING_FORMAT("invalid string value for float4: %s.", this->AsCharPtr()),
GET_FUNCTION_NAME()
);
}
Math::float4 v(tokens[0].AsFloat(), tokens[1].AsFloat(), tokens[2].AsFloat(), tokens[3].AsFloat());
return v;
}
//------------------------------------------------------------------------------
/**
Returns content as bbox. Note: this method doesn't check whether the
contents is actually a valid bbox. Use the IsValidBBox() method
for this!
*/
Math::bbox
String::AsBBox() const
{
Array<String> tokens(6, 0);
this->Tokenize(", \t", tokens);
if(tokens.Size() != 6)
{
SYS_EXCEPT(Exceptions::FormatException,
STRING_FORMAT("invalid string value for bbox: %s.", this->AsCharPtr()),
GET_FUNCTION_NAME()
);
}
Math::bbox v;
v.pmin.set(tokens[0].AsFloat(), tokens[1].AsFloat(), tokens[2].AsFloat() );
v.pmax.set(tokens[3].AsFloat(), tokens[4].AsFloat(), tokens[5].AsFloat() );
return v;
}
//------------------------------------------------------------------------------
/**
*/
Util::AssetPath String::AsAssetPath() const
{
Array<String> tokens(2, 0);
int idx = 0;
const char* ptr = this->AsCharPtr();
for ( int i = this->strLen - 1; i >= 0 ; --i)
{
if( *(ptr+i) == ',')
{
idx = i;
break;
}
}
Util::String path = this->EraseRange(idx, this->Length() - idx);
Util::String type = this->EraseRange(0, idx+1);
tokens.Append(path);
tokens.Append(type);
if(tokens.Size() != 2)
{
SYS_EXCEPT(Exceptions::FormatException,
STRING_FORMAT("invalid string value for assetPath: %s.", this->AsCharPtr()),
GET_FUNCTION_NAME()
);
}
Util::AssetPath v;
v.path = tokens[0];
v.type = tokens[1].AsInt();
return v;
}
//------------------------------------------------------------------------------
/**
*/
bool
String::IsDigit(char c)
{
return (0 != isdigit(int(c)));
}
//------------------------------------------------------------------------------
/**
*/
bool
String::IsAlpha(char c)
{
return (0 != isalpha(int(c)));
}
//------------------------------------------------------------------------------
/**
*/
bool
String::IsAlNum(char c)
{
return (0 != isalnum(int(c)));
}
//------------------------------------------------------------------------------
/**
*/
bool
String::IsLower(char c)
{
return (0 != islower(int(c)));
}
//------------------------------------------------------------------------------
/**
*/
bool
String::IsUpper(char c)
{
return (0 != isupper(int(c)));
}
//------------------------------------------------------------------------------
/**
*/
bool
String::IsSpace(char c)
{
return (0 != isspace(int(c)));
}
//------------------------------------------------------------------------------
/**
*/
int
String::StrCmp(const char* str0, const char* str1)
{
n_assert(str0 && str1);
return strcmp(str0, str1);
}
//------------------------------------------------------------------------------
/**
*/
int
String::StrLen(const char* str)
{
n_assert(str);
return strlen(str);
}
//------------------------------------------------------------------------------
/**
*/
const char*
String::StrChr(const char* str, int c)
{
n_assert(str);
return strchr(str, c);
}
//------------------------------------------------------------------------------
/**
*/
Dictionary<String,String>
String::ParseKeyValuePairs(const String& str)
{
Dictionary<String,String> res;
Array<String> tokens = str.Tokenize(" \t\n=", '"');
n_assert(0 == (tokens.Size() & 1)); // num tokens must be even
IndexT i;
for (i = 0; i < tokens.Size(); i += 2)
{
res.Add(tokens[i], tokens[i + 1]);
}
return res;
}
//------------------------------------------------------------------------------
/**
*/
void
String::ChangeFileExtension(const Util::String& newExt)
{
this->StripFileExtension();
this->Append("." + newExt);
}
} // namespace System