/**************************************************************************** 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 #include #include #include 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& outTokens) const { outTokens.Clear(); String str(*this); char* ptr = const_cast(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::Tokenize(const String& whiteSpace) const { Array 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& outTokens) const { outTokens.Clear(); String str(*this); char* ptr = const_cast(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::Tokenize(const String& whiteSpace, char fence) const { Array 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(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( startIndexstrLen ); 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(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 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& 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(this->AsCharPtr()); ptr[index] = 0; this->strLen = (SizeT) strlen(ptr); } //------------------------------------------------------------------------------ /** Remove the file extension. */ void String::StripFileExtension() { char* str = const_cast(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(this->AsCharPtr()); char c; while (0 != (c = *str)) { *str++ = (char) tolower(c); } } //------------------------------------------------------------------------------ /** */ void String::ToUpper() { char* str = const_cast(this->AsCharPtr()); char c; while (0 != (c = *str)) { *str++ = (char) toupper(c); } } //------------------------------------------------------------------------------ /** */ void String::FirstCharToUpper() { char* str = const_cast(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(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(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 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 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 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 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 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::ParseKeyValuePairs(const String& str) { Dictionary res; Array 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