diff options
-rw-r--r-- | indra/llcommon/lldate.cpp | 10 | ||||
-rw-r--r-- | indra/llcommon/llstring.cpp | 319 | ||||
-rw-r--r-- | indra/llcommon/llstring.h | 301 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 2 |
4 files changed, 329 insertions, 303 deletions
diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp index 7bc9e16bc9..6a82a848be 100644 --- a/indra/llcommon/lldate.cpp +++ b/indra/llcommon/lldate.cpp @@ -94,8 +94,12 @@ std::string LLDate::asRFC1123() const return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT")); } +LLFastTimer::DeclareTimer FT_DATE_FORMAT("Date Format"); + std::string LLDate::toHTTPDateString (std::string fmt) const { + LLFastTimer ft1(FT_DATE_FORMAT); + std::ostringstream stream; time_t locSeconds = (time_t) mSecondsSinceEpoch; struct tm * gmt = gmtime (&locSeconds); @@ -107,6 +111,8 @@ std::string LLDate::toHTTPDateString (std::string fmt) const std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt) { + LLFastTimer ft1(FT_DATE_FORMAT); + std::ostringstream stream; stream.imbue (std::locale(LLStringUtil::getLocale().c_str())); toHTTPDateStream (stream, gmt, fmt); @@ -115,11 +121,11 @@ std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt) void LLDate::toHTTPDateStream(std::ostream& s, tm * gmt, std::string fmt) { - using namespace std; + LLFastTimer ft1(FT_DATE_FORMAT); const char * pBeg = fmt.c_str(); const char * pEnd = pBeg + fmt.length(); - const time_put<char>& tp = use_facet<time_put<char> >(s.getloc()); + const std::time_put<char>& tp = std::use_facet<std::time_put<char> >(s.getloc()); tp.put (s, s, s.fill(), gmt, pBeg, pEnd); } diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index d7da40d645..16cfb2fc25 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -42,7 +42,7 @@ #include <winnls.h> // for WideCharToMultiByte #endif -LLFastTimer::DeclareTimer STRING_LOCALIZATION("String Localization"); +LLFastTimer::DeclareTimer FT_STRING_FORMAT("String Format"); std::string ll_safe_string(const char* in) @@ -776,12 +776,12 @@ namespace LLStringFn // https://wiki.lindenlab.com/wiki/Unicode_Guidelines has details on // allowable code points for XML. Specifically, they are: // 0x09, 0x0a, 0x0d, and 0x20 on up. JC - std::string strip_invalid_xml(const std::string& input) + std::string strip_invalid_xml(const std::string& instr) { std::string output; - output.reserve( input.size() ); - std::string::const_iterator it = input.begin(); - while (it != input.end()) + output.reserve( instr.size() ); + std::string::const_iterator it = instr.begin(); + while (it != instr.end()) { // Must compare as unsigned for >= // Test most likely match first @@ -817,6 +817,315 @@ namespace LLStringFn } } +//////////////////////////////////////////////////////////// + +//static +template<> +void LLStringUtil::getTokens(const std::string& instr, std::vector<std::string >& tokens, const std::string& delims) +{ + std::string currToken; + std::string::size_type begIdx, endIdx; + + begIdx = instr.find_first_not_of (delims); + while (begIdx != std::string::npos) + { + endIdx = instr.find_first_of (delims, begIdx); + if (endIdx == std::string::npos) + { + endIdx = instr.length(); + } + + currToken = instr.substr(begIdx, endIdx - begIdx); + LLStringUtil::trim (currToken); + tokens.push_back(currToken); + begIdx = instr.find_first_not_of (delims, endIdx); + } +} + +template<> +LLStringUtil::size_type LLStringUtil::getSubstitution(const std::string& instr, size_type& start, std::vector<std::string>& tokens) +{ + const std::string delims (","); + + // Find the first ] + size_type pos2 = instr.find(']', start); + if (pos2 == std::string::npos) + return std::string::npos; + + // Find the last [ before ] + size_type pos1 = instr.find_last_of('[', pos2-1); + if (pos1 == std::string::npos || pos1 < start) + return std::string::npos; + + getTokens(std::string(instr,pos1+1,pos2-pos1-1), tokens, delims); + start = pos2+1; + + return pos1; +} + +// LLStringUtil::format recogizes the following patterns. +// All substitutions *must* be encased in []'s in the input string. +// The []'s are optional in the substitution map. +// [FOO_123] +// [FOO,number,precision] +// [FOO,datetime,format] + + +// static +template<> +S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) +{ + LLFastTimer ft(FT_STRING_FORMAT); + S32 res = 0; + + std::string output; + std::vector<std::string> tokens; + + std::string::size_type start = 0; + std::string::size_type prev_start = 0; + std::string::size_type key_start = 0; + while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos) + { + output += std::string(s, prev_start, key_start-prev_start); + prev_start = start; + + bool found_replacement = false; + std::string replacement; + + if (tokens.size() == 1) + { + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + } + else if (tokens[1] == "number") + { + std::string param = "0"; + + if (tokens.size() > 2) param = tokens[2]; + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + if (found_replacement) formatNumber (replacement, param); + } + else if (tokens[1] == "datetime") + { + std::string param; + if (tokens.size() > 2) param = tokens[2]; + + format_map_t::const_iterator iter = substitutions.find("datetime"); + if (iter != substitutions.end()) + { + S32 secFromEpoch = 0; + BOOL r = LLStringUtil::convertToS32(iter->second, secFromEpoch); + if (r) + { + found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch); + } + } + } + + if (found_replacement) + { + output += replacement; + res++; + } + else + { + // we had no replacement, so leave the string we searched for so that it gets noticed by QA + // "hello [NAME_NOT_FOUND]" is output + output += std::string("[") + tokens[0] + std::string("]"); + } + tokens.clear(); + } + // send the remainder of the string (with no further matches for bracketed names) + output += std::string(s, start); + s = output; + return res; +} + +//static +template<> +S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) +{ + LLFastTimer ft(FT_STRING_FORMAT); + S32 res = 0; + + if (!substitutions.isMap()) + { + return res; + } + + std::string output; + std::vector<std::string> tokens; + + std::string::size_type start = 0; + std::string::size_type prev_start = 0; + std::string::size_type key_start = 0; + while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos) + { + output += std::string(s, prev_start, key_start-prev_start); + prev_start = start; + + bool found_replacement = false; + std::string replacement; + + if (tokens.size() == 1) + { + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + } + else if (tokens[1] == "number") + { + std::string param = "0"; + + if (tokens.size() > 2) param = tokens[2]; + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + if (found_replacement) formatNumber (replacement, param); + } + else if (tokens[1] == "datetime") + { + std::string param; + if (tokens.size() > 2) param = tokens[2]; + + S32 secFromEpoch = (S32) substitutions["datetime"].asInteger(); + found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch); + } + + if (found_replacement) + { + output += replacement; + res++; + } + else + { + // we had no replacement, so leave the string we searched for so that it gets noticed by QA + // "hello [NAME_NOT_FOUND]" is output + output += std::string("[") + tokens[0] + std::string("]"); + } + tokens.clear(); + } + // send the remainder of the string (with no further matches for bracketed names) + output += std::string(s, start); + s = output; + return res; +} + +// static +template<> +bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const format_map_t& substitutions) +{ + // see if we have a replacement for the bracketed string (without the brackets) + // test first using has() because if we just look up with operator[] we get back an + // empty string even if the value is missing. We want to distinguish between + // missing replacements and deliberately empty replacement strings. + format_map_t::const_iterator iter = substitutions.find(token); + if (iter != substitutions.end()) + { + replacement = iter->second; + return true; + } + // if not, see if there's one WITH brackets + iter = substitutions.find(std::string("[" + token + "]")); + if (iter != substitutions.end()) + { + replacement = iter->second; + return true; + } + + return false; +} + +// static +template<> +bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const LLSD& substitutions) +{ + // see if we have a replacement for the bracketed string (without the brackets) + // test first using has() because if we just look up with operator[] we get back an + // empty string even if the value is missing. We want to distinguish between + // missing replacements and deliberately empty replacement strings. + if (substitutions.has(token)) + { + replacement = substitutions[token].asString(); + return true; + } + // if not, see if there's one WITH brackets + else if (substitutions.has(std::string("[" + token + "]"))) + { + replacement = substitutions[std::string("[" + token + "]")].asString(); + return true; + } + + return false; +} + +// static +template<> +void LLStringUtil::formatNumber(std::string& numStr, std::string decimals) +{ + std::stringstream strStream; + S32 intDecimals = 0; + + convertToS32 (decimals, intDecimals); + if (!sLocale.empty()) + { + strStream.imbue (std::locale(sLocale.c_str())); + } + + if (!intDecimals) + { + S32 intStr; + + if (convertToS32(numStr, intStr)) + { + strStream << intStr; + numStr = strStream.str(); + } + } + else + { + F32 floatStr; + + if (convertToF32(numStr, floatStr)) + { + strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr; + numStr = strStream.str(); + } + } +} + +// static +template<> +bool LLStringUtil::formatDatetime(std::string& replacement, std::string token, + std::string param, S32 secFromEpoch) +{ + if (param == "local") // local + { + secFromEpoch -= LLStringOps::getLocalTimeOffset(); + } + else if (param != "utc") // slt + { + secFromEpoch -= LLStringOps::getSltOffset(); + } + + // if never fell into those two ifs above, param must be utc + if (secFromEpoch < 0) secFromEpoch = 0; + + LLDate * datetime = new LLDate((F64)secFromEpoch); + std::string code = LLStringOps::getDatetimeCode (token); + + // special case to handle timezone + if (code == "%Z") { + if (param == "utc") replacement = "GMT"; + else if (param != "local") replacement = LLStringOps::getDaylightSavings()? "PDT" : "PST"; + return true; + } + replacement = datetime->toHTTPDateString(code); + + if (code.empty()) + { + return false; + } + else + { + return true; + } +} //////////////////////////////////////////////////////////// // Testing diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index c6dcdd6d12..3b1379c76a 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -231,7 +231,8 @@ public: static std::basic_string<T> null; typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t; - static void getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens); + static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims); + static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens); static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals); static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch); static S32 format(std::basic_string<T>& s, const format_map_t& substitutions); @@ -597,302 +598,12 @@ namespace LLStringFn } //////////////////////////////////////////////////////////// +// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp. +// There is no LLWStringUtil::format implementation currently. +// Calling thse for anything other than LLStringUtil will produce link errors. -//static -template<class T> -void LLStringUtilBase<T>::getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens) -{ - const std::basic_string<T> delims (","); - std::basic_string<T> currToken; - size_type begIdx, endIdx; - - begIdx = input.find_first_not_of (delims); - while (begIdx != std::basic_string<T>::npos) - { - endIdx = input.find_first_of (delims, begIdx); - if (endIdx == std::basic_string<T>::npos) - { - endIdx = input.length(); - } - - currToken = input.substr(begIdx, endIdx - begIdx); - trim (currToken); - tokens.push_back(currToken); - begIdx = input.find_first_not_of (delims, endIdx); - } -} - -extern LLFastTimer::DeclareTimer STRING_LOCALIZATION; - -// static -template<class T> -S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const format_map_t& substitutions) -{ - LLFastTimer ft(STRING_LOCALIZATION); - S32 res = 0; - - std::basic_ostringstream<T> output; - // match strings like [NAME,number,3] - const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]"); - - - typename std::basic_string<T>::const_iterator start = s.begin(); - typename std::basic_string<T>::const_iterator end = s.end(); - boost::smatch match; - - - while (boost::regex_search(start, end, match, key, boost::match_default)) - { - bool found_replacement = false; - std::vector<std::basic_string<T> > tokens; - std::basic_string<T> replacement; - - getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens); - - if (tokens.size() == 1) - { - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - } - else if (tokens[1] == "number") - { - std::basic_string<T> param = "0"; - - if (tokens.size() > 2) param = tokens[2]; - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - if (found_replacement) formatNumber (replacement, param); - } - else if (tokens[1] == "datetime") - { - std::basic_string<T> param; - if (tokens.size() > 2) param = tokens[2]; - - format_map_t::const_iterator iter = substitutions.find("datetime"); - if (iter != substitutions.end()) - { - S32 secFromEpoch = 0; - BOOL r = LLStringUtil::convertToS32(iter->second, secFromEpoch); - if (r) - { - found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch); - } - } - } - - if (found_replacement) - { - output << std::basic_string<T>(start, match[0].first) << replacement; - res++; - } - else - { - // we had no replacement, so leave the string we searched for so that it gets noticed by QA - // "hello [NAME_NOT_FOUND]" is output - output << std::basic_string<T>(start, match[0].second); - } - - // update search position - start = match[0].second; - } - // send the remainder of the string (with no further matches for bracketed names) - output << std::basic_string<T>(start, end); - s = output.str(); - return res; -} - -//static -template<class T> -S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutions) -{ - LLFastTimer ft(STRING_LOCALIZATION); - - S32 res = 0; - - if (!substitutions.isMap()) - { - return res; - } - - std::basic_ostringstream<T> output; - // match strings like [NAME,number,3] - const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]"); - - - typename std::basic_string<T>::const_iterator start = s.begin(); - typename std::basic_string<T>::const_iterator end = s.end(); - boost::smatch match; - - - while (boost::regex_search(start, end, match, key, boost::match_default)) - { - bool found_replacement = false; - std::vector<std::basic_string<T> > tokens; - std::basic_string<T> replacement; - - getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens); - - if (tokens.size() == 1) - { - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - } - else if (tokens[1] == "number") - { - std::basic_string<T> param = "0"; - - if (tokens.size() > 2) param = tokens[2]; - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - if (found_replacement) formatNumber (replacement, param); - } - else if (tokens[1] == "datetime") - { - std::basic_string<T> param; - if (tokens.size() > 2) param = tokens[2]; - - S32 secFromEpoch = (S32) substitutions["datetime"].asInteger(); - found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch); - } - - if (found_replacement) - { - output << std::basic_string<T>(start, match[0].first) << replacement; - res++; - } - else - { - // we had no replacement, so leave the string we searched for so that it gets noticed by QA - // "hello [NAME_NOT_FOUND]" is output - output << std::basic_string<T>(start, match[0].second); - } - - // update search position - start = match[0].second; - } - // send the remainder of the string (with no further matches for bracketed names) - output << std::basic_string<T>(start, end); - s = output.str(); - return res; -} - -// static -template<class T> -bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const format_map_t& substitutions) -{ - // see if we have a replacement for the bracketed string (without the brackets) - // test first using has() because if we just look up with operator[] we get back an - // empty string even if the value is missing. We want to distinguish between - // missing replacements and deliberately empty replacement strings. - format_map_t::const_iterator iter = substitutions.find(token); - if (iter != substitutions.end()) - { - replacement = iter->second; - return true; - } - // if not, see if there's one WITH brackets - iter = substitutions.find(std::basic_string<T>("[" + token + "]")); - if (iter != substitutions.end()) - { - replacement = iter->second; - return true; - } - - return false; -} - -// static -template<class T> -bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const LLSD& substitutions) -{ - // see if we have a replacement for the bracketed string (without the brackets) - // test first using has() because if we just look up with operator[] we get back an - // empty string even if the value is missing. We want to distinguish between - // missing replacements and deliberately empty replacement strings. - if (substitutions.has(token)) - { - replacement = substitutions[token].asString(); - return true; - } - // if not, see if there's one WITH brackets - else if (substitutions.has(std::basic_string<T>("[" + token + "]"))) - { - replacement = substitutions[std::basic_string<T>("[" + token + "]")].asString(); - return true; - } - - return false; -} - -// static -template<class T> -void LLStringUtilBase<T>::formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals) -{ - typedef typename std::basic_string<T>::size_type string_size_type_t; - std::basic_stringstream<T> strStream; - S32 intDecimals = 0; - - convertToS32 (decimals, intDecimals); - if (!sLocale.empty()) - { - strStream.imbue (std::locale(sLocale.c_str())); - } - - if (!intDecimals) - { - S32 intStr; - - if (convertToS32(numStr, intStr)) - { - strStream << intStr; - numStr = strStream.str(); - } - } - else - { - F32 floatStr; - - if (convertToF32(numStr, floatStr)) - { - strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr; - numStr = strStream.str(); - } - } -} - -// static -template<class T> -bool LLStringUtilBase<T>::formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, - std::basic_string<T> param, S32 secFromEpoch) -{ - if (param == "local") // local - { - secFromEpoch -= LLStringOps::getLocalTimeOffset(); - } - else if (param != "utc") // slt - { - secFromEpoch -= LLStringOps::getSltOffset(); - } - - // if never fell into those two ifs above, param must be utc - if (secFromEpoch < 0) secFromEpoch = 0; - - LLDate * datetime = new LLDate((F64)secFromEpoch); - std::string code = LLStringOps::getDatetimeCode (token); - - // special case to handle timezone - if (code == "%Z") { - if (param == "utc") replacement = "GMT"; - else if (param != "local") replacement = LLStringOps::getDaylightSavings()? "PDT" : "PST"; - return true; - } +//////////////////////////////////////////////////////////// - replacement = datetime->toHTTPDateString(code); - if (code.empty()) - { - return false; - } - else - { - return true; - } -} // static template<class T> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 3c66f99e6a..8b20918700 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -5029,7 +5029,7 @@ You must have payment information on file to visit this area. Do you want to go icon="alertmodal.tga" name="MissingString" type="alertmodal"> -How odd. The string [STRING_NAME] is missing from strings.xml +The string [STRING_NAME] is missing from strings.xml </notification> <notification |