| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- #include "CmStringTable.h"
- #include "CmException.h"
- namespace CamelotFramework
- {
- const Language StringTable::DEFAULT_LANGUAGE = Language::EnglishUS;
- LocalizedStringData::LocalizedStringData()
- :commonData(nullptr), parameterOffsets(nullptr), numParameters(0)
- {
- }
- LocalizedStringData::~LocalizedStringData()
- {
- if(parameterOffsets != nullptr)
- cm_deleteN(parameterOffsets, numParameters);
- }
- WString LocalizedStringData::concatenateString(WString* parameters, UINT32 numParameterValues) const
- {
- WStringStream fullString;
- UINT32 prevIdx = 0;
- // A safeguard in case translated strings have different number of parameters
- UINT32 actualNumParameters = std::min(numParameterValues, numParameters);
- if(parameters != nullptr)
- {
- for(UINT32 i = 0; i < actualNumParameters; i++)
- {
- fullString<<string.substr(prevIdx, parameterOffsets[i].location - prevIdx);
- fullString<<parameters[parameterOffsets[i].paramIdx];
- prevIdx = parameterOffsets[i].location;
- }
- }
- fullString<<string.substr(prevIdx, string.size() - prevIdx);
- return fullString.str();
- }
- void LocalizedStringData::updateString(const WString& _string)
- {
- if(parameterOffsets != nullptr)
- cm_deleteN(parameterOffsets, numParameters);
- Vector<ParamOffset>::type paramOffsets;
- INT32 lastBracket = -1;
- WStringStream bracketChars;
- WStringStream cleanString;
- bool escaped = false;
- UINT32 numRemovedChars = 0;
- for(UINT32 i = 0; i < (UINT32)_string.size(); i++)
- {
- if(_string[i] == '\\' && !escaped)
- {
- numRemovedChars++;
- escaped = true;
- continue;
- }
- if(lastBracket == -1)
- {
- // If current char is non-escaped opening bracket start parameter definition
- if(_string[i] == '{' && !escaped)
- lastBracket = i;
- else
- cleanString<<_string[i];
- }
- else
- {
- if(isdigit(_string[i]))
- bracketChars<<_string[i];
- else
- {
- // If current char is non-escaped closing bracket end parameter definition
- UINT32 numParamChars = (UINT32)bracketChars.tellp();
- if(_string[i] == '}' && numParamChars > 0 && !escaped)
- {
- numRemovedChars += numParamChars + 2; // +2 for open and closed brackets
- UINT32 paramIdx = parseUnsignedInt(bracketChars.str());
- paramOffsets.push_back(ParamOffset(paramIdx, i + 1 - numRemovedChars));
- }
- else
- {
- // Last bracket wasn't really a parameter
- for(UINT32 j = lastBracket; j <= i; j++)
- cleanString<<_string[j];
- }
- lastBracket = -1;
- bracketChars.clear();
- }
- }
- escaped = false;
- }
- string = cleanString.str();
- numParameters = (UINT32)paramOffsets.size();
- // Try to find out of order param offsets and fix them
- std::sort(begin(paramOffsets), end(paramOffsets),
- [&] (const ParamOffset& a, const ParamOffset& b) { return a.paramIdx < b.paramIdx; } );
- if(paramOffsets.size() > 0)
- {
- UINT32 sequentialIdx = 0;
- UINT32 lastParamIdx = paramOffsets[0].paramIdx;
- for(UINT32 i = 0; i < numParameters; i++)
- {
- if(paramOffsets[i].paramIdx == lastParamIdx)
- {
- paramOffsets[i].paramIdx = sequentialIdx;
- continue;
- }
- lastParamIdx = paramOffsets[i].paramIdx;
- sequentialIdx++;
- paramOffsets[i].paramIdx = sequentialIdx;
- }
- }
- // Re-sort based on location since we find that more useful at runtime
- std::sort(begin(paramOffsets), end(paramOffsets),
- [&] (const ParamOffset& a, const ParamOffset& b) { return a.location < b.location; } );
- parameterOffsets = cm_newN<ParamOffset>(numParameters);
- for(UINT32 i = 0; i < numParameters; i++)
- parameterOffsets[i] = paramOffsets[i];
- }
- StringTable::StringTable()
- :mActiveLanguageData(nullptr), mDefaultLanguageData(nullptr), mAllLanguages(nullptr)
- {
- mAllLanguages = cm_newN<LanguageData>((UINT32)Language::Count);
- mDefaultLanguageData = &(mAllLanguages[(UINT32)DEFAULT_LANGUAGE]);
- mActiveLanguageData = mDefaultLanguageData;
- mActiveLanguage = DEFAULT_LANGUAGE;
- }
-
- StringTable::~StringTable()
- {
- for(UINT32 i = 0; i < (UINT32)Language::Count; i++)
- {
- for(auto& iter : mAllLanguages[i].strings)
- cm_delete(iter.second);
- }
- cm_deleteN(mAllLanguages, (UINT32)Language::Count);
- for(auto& common : mCommonData)
- cm_delete(common.second);
- }
- void StringTable::setActiveLanguage(Language language)
- {
- if(language == mActiveLanguage)
- return;
- mActiveLanguageData = &(mAllLanguages[(UINT32)language]);
- mActiveLanguage = language;
- notifyAllStringsChanged();
- }
- void StringTable::setString(const WString& identifier, Language language, const WString& string)
- {
- LanguageData* curLanguage = &(mAllLanguages[(UINT32)language]);
- auto iterFind = curLanguage->strings.find(identifier);
- LocalizedStringData* stringData;
- if(iterFind == curLanguage->strings.end())
- {
- auto iterFindCommon = mCommonData.find(identifier);
- LocalizedStringData::Common* common = nullptr;
- if(iterFindCommon == mCommonData.end())
- {
- common = cm_new<LocalizedStringData::Common>();
- common->identifier = identifier;
- mCommonData[identifier] = common;
- }
- else
- common = iterFindCommon->second;
- stringData = cm_new<LocalizedStringData>();
- curLanguage->strings[identifier] = stringData;
- stringData->commonData = common;
- }
- else
- {
- stringData = iterFind->second;
- }
- stringData->updateString(string);
- if(mActiveLanguage == language)
- {
- if(!stringData->commonData->onStringDataModified.empty())
- stringData->commonData->onStringDataModified();
- }
- }
- void StringTable::removeString(const WString& identifier)
- {
- // Order of operations is very important here, in case a string that is in use
- // is removed. In that case we want the string to be marked as modified and it should
- // call getStringData which will generate a new entry for the string.
-
- LocalizedStringData* stringData = nullptr;
- for(UINT32 i = 0; i < (UINT32)Language::Count; i++)
- {
- auto findIter = mAllLanguages[i].strings.find(identifier);
- if(findIter != mAllLanguages[i].strings.end())
- {
- if(mActiveLanguage == (Language)i)
- stringData = findIter->second;
- else
- cm_delete(findIter->second);
- mAllLanguages[i].strings.erase(findIter);
- }
- }
- auto findIterCommon = mCommonData.find(identifier);
- LocalizedStringData::Common* common = nullptr;
- if(findIterCommon != mCommonData.end())
- {
- common = findIterCommon->second;
- mCommonData.erase(findIterCommon);
- if(!common->onStringDataModified.empty())
- common->onStringDataModified();
- }
- if(stringData != nullptr)
- cm_delete(stringData);
- if(common != nullptr)
- cm_delete(common);
- }
- LocalizedStringData& StringTable::getStringData(const WString& identifier, bool insertIfNonExisting)
- {
- return getStringData(identifier, mActiveLanguage, insertIfNonExisting);
- }
- LocalizedStringData& StringTable::getStringData(const WString& identifier, Language language, bool insertIfNonExisting)
- {
- LanguageData* curLanguage = &(mAllLanguages[(UINT32)language]);
- auto iterFind = curLanguage->strings.find(identifier);
- if(iterFind != curLanguage->strings.end())
- return *iterFind->second;
- auto defaultIterFind = mDefaultLanguageData->strings.find(identifier);
- if(defaultIterFind != mDefaultLanguageData->strings.end())
- return *defaultIterFind->second;
- if(insertIfNonExisting)
- {
- setString(identifier, DEFAULT_LANGUAGE, identifier);
- auto defaultIterFind = mDefaultLanguageData->strings.find(identifier);
- if(defaultIterFind != mDefaultLanguageData->strings.end())
- return *defaultIterFind->second;
- }
- CM_EXCEPT(InvalidParametersException, "There is no string data for the provided identifier.");
- }
- void StringTable::notifyAllStringsChanged()
- {
- for(auto& iter : mCommonData)
- {
- if(!iter.second->onStringDataModified.empty())
- iter.second->onStringDataModified();
- }
- }
- }
|