diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/binary.cpp | 18 | ||||
-rw-r--r-- | lib/binary.h | 2 | ||||
-rw-r--r-- | lib/dir_exist_async.h | 2 | ||||
-rw-r--r-- | lib/localization.cpp | 124 | ||||
-rw-r--r-- | lib/lock_holder.h | 2 | ||||
-rw-r--r-- | lib/parse_lng.h | 191 | ||||
-rw-r--r-- | lib/parse_plural.h | 524 | ||||
-rw-r--r-- | lib/perf_check.cpp | 23 | ||||
-rw-r--r-- | lib/perf_check.h | 1 | ||||
-rw-r--r-- | lib/process_xml.cpp | 95 | ||||
-rw-r--r-- | lib/process_xml.h | 17 | ||||
-rw-r--r-- | lib/resources.cpp | 4 | ||||
-rw-r--r-- | lib/status_handler.cpp | 2 | ||||
-rw-r--r-- | lib/status_handler.h | 2 | ||||
-rw-r--r-- | lib/versioning.cpp | 82 | ||||
-rw-r--r-- | lib/versioning.h | 32 |
16 files changed, 589 insertions, 532 deletions
diff --git a/lib/binary.cpp b/lib/binary.cpp index 10994cc9..4ef30c15 100644 --- a/lib/binary.cpp +++ b/lib/binary.cpp @@ -85,7 +85,6 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename FileInput file2(filename2); // BufferSize bufferSize; - UInt64 bytesCompared; TickVal lastDelayViolation = getTicks(); @@ -96,14 +95,19 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename const TickVal startTime = getTicks(); - const size_t length1 = file1.read(&memory1[0], bufferSize); //returns actual number of bytes read; throw FileError() - const size_t length2 = file2.read(&memory2[0], bufferSize); // + const size_t length1 = file1.read(&memory1[0], bufferSize); //throw FileError() + const size_t length2 = file2.read(&memory2[0], bufferSize); //returns actual number of bytes read + //send progress updates immediately after reading to reliably allow speed calculations for our clients! + callback.updateCompareStatus(to<Int64>(std::max(length1, length2))); - const TickVal now = getTicks(); + if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) + return false; //-------- dynamically set buffer size to keep callback interval between 100 - 500ms --------------------- if (TICKS_PER_SEC > 0) { + const TickVal now = getTicks(); + const std::int64_t loopTime = dist(startTime, now) * 1000 / TICKS_PER_SEC; //unit: [ms] if (loopTime < 100) { @@ -120,12 +124,6 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename } } //------------------------------------------------------------------------------------------------ - - if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) - return false; - - bytesCompared += length1; - callback.updateCompareStatus(bytesCompared); //send progress updates } while (!file1.eof()); diff --git a/lib/binary.h b/lib/binary.h index 83409e19..8a4abe6b 100644 --- a/lib/binary.h +++ b/lib/binary.h @@ -16,7 +16,7 @@ namespace zen struct CompareCallback { virtual ~CompareCallback() {} - virtual void updateCompareStatus(UInt64 totalBytes) = 0; + virtual void updateCompareStatus(Int64 bytesDelta) = 0; }; bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback); //throw FileError diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h index a02facad..306c9ab9 100644 --- a/lib/dir_exist_async.h +++ b/lib/dir_exist_async.h @@ -31,7 +31,7 @@ bool dirExistsUpdating(const Zstring& dirname, bool allowUserInteraction, Proces //2. check dir existence return zen::dirExists(dirname); }); - while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL))) + while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2))) procCallback.requestUiRefresh(); //may throw! return ft.get(); } diff --git a/lib/localization.cpp b/lib/localization.cpp index 47ed1881..78e03545 100644 --- a/lib/localization.cpp +++ b/lib/localization.cpp @@ -28,23 +28,22 @@ namespace class FFSLocale : public TranslationHandler { public: - FFSLocale(const wxString& filename, wxLanguage languageId); //throw lngfile::ParsingError, PluralForm::ParsingError + FFSLocale(const wxString& filename, wxLanguage languageId); //throw lngfile::ParsingError, parse_plural::ParsingError wxLanguage langId() const { return langId_; } virtual std::wstring translate(const std::wstring& text) { //look for translation in buffer table - const Translation::const_iterator it = transMapping.find(text); - if (it != transMapping.end()) + auto it = transMapping.find(text); + if (it != transMapping.end() && !it->second.empty()) return it->second; - return text; //fallback } virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, int n) { - TranslationPlural::const_iterator it = transMappingPl.find(std::make_pair(singular, plural)); + auto it = transMappingPl.find(std::make_pair(singular, plural)); if (it != transMappingPl.end()) { const int formNo = pluralParser->getForm(n); @@ -55,17 +54,17 @@ public: } private: - typedef std::map<std::wstring, std::wstring> Translation; + typedef hash_map<std::wstring, std::wstring> Translation; //hash_map is 15% faster than std::map on GCC typedef std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring> > TranslationPlural; Translation transMapping; //map original text |-> translation TranslationPlural transMappingPl; - std::unique_ptr<PluralForm> pluralParser; + std::unique_ptr<parse_plural::PluralForm> pluralParser; //bound! wxLanguage langId_; }; -FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, PluralForm::ParsingError +FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_(languageId) //throw lngfile::ParsingError, parse_plural::ParsingError { std::string inputStream; try @@ -86,7 +85,6 @@ FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_( { const std::wstring original = utfCvrtTo<std::wstring>(i->first); const std::wstring translation = utfCvrtTo<std::wstring>(i->second); - assert(!translation.empty()); transMapping.insert(std::make_pair(original, translation)); } @@ -100,13 +98,10 @@ FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_( for (lngfile::PluralForms::const_iterator j = plForms.begin(); j != plForms.end(); ++j) plFormsWide.push_back(utfCvrtTo<std::wstring>(*j)); - assert(!plFormsWide.empty()); - transMappingPl.insert(std::make_pair(std::make_pair(singular, plural), plFormsWide)); } - pluralParser.reset(new PluralForm(header.pluralDefinition)); //throw PluralForm::ParsingError -} + pluralParser.reset(new parse_plural::PluralForm(header.pluralDefinition)); //throw parse_plural::ParsingError } @@ -151,6 +146,7 @@ struct LessTranslation : public std::binary_function<ExistingTranslations::Entry #endif } }; +} ExistingTranslations::ExistingTranslations() @@ -173,10 +169,10 @@ ExistingTranslations::ExistingTranslations() traverseFolder(zen::getResourceDir() + Zstr("Languages"), //throw(); traverseCallback); - for (auto i = lngFiles.begin(); i != lngFiles.end(); ++i) + for (auto it = lngFiles.begin(); it != lngFiles.end(); ++it) try { - std::string stream = loadStream(*i); //throw XmlFileError + std::string stream = loadStream(*it); //throw XmlFileError try { lngfile::TransHeader lngHeader; @@ -191,7 +187,7 @@ ExistingTranslations::ExistingTranslations() ExistingTranslations::Entry newEntry; newEntry.languageID = locInfo->Language; newEntry.languageName = utfCvrtTo<wxString>(lngHeader.languageName); - newEntry.languageFile = utfCvrtTo<wxString>(*i); + newEntry.languageFile = utfCvrtTo<wxString>(*it); newEntry.translatorName = utfCvrtTo<wxString>(lngHeader.translatorName); newEntry.languageFlag = utfCvrtTo<wxString>(lngHeader.flagFile); locMapping.push_back(newEntry); @@ -205,12 +201,59 @@ ExistingTranslations::ExistingTranslations() } +const std::vector<ExistingTranslations::Entry>& ExistingTranslations::get() +{ + static ExistingTranslations instance; + return instance.locMapping; +} + + namespace { wxLanguage mapLanguageDialect(wxLanguage language) { - switch (static_cast<int>(language)) //map language dialects + switch (static_cast<int>(language)) //avoid enumeration value wxLANGUAGE_*' not handled in switch [-Wswitch-enum] { + //variants of wxLANGUAGE_ARABIC + case wxLANGUAGE_ARABIC_ALGERIA: + case wxLANGUAGE_ARABIC_BAHRAIN: + case wxLANGUAGE_ARABIC_EGYPT: + case wxLANGUAGE_ARABIC_IRAQ: + case wxLANGUAGE_ARABIC_JORDAN: + case wxLANGUAGE_ARABIC_KUWAIT: + case wxLANGUAGE_ARABIC_LEBANON: + case wxLANGUAGE_ARABIC_LIBYA: + case wxLANGUAGE_ARABIC_MOROCCO: + case wxLANGUAGE_ARABIC_OMAN: + case wxLANGUAGE_ARABIC_QATAR: + case wxLANGUAGE_ARABIC_SAUDI_ARABIA: + case wxLANGUAGE_ARABIC_SUDAN: + case wxLANGUAGE_ARABIC_SYRIA: + case wxLANGUAGE_ARABIC_TUNISIA: + case wxLANGUAGE_ARABIC_UAE: + case wxLANGUAGE_ARABIC_YEMEN: + return wxLANGUAGE_ARABIC; + + //variants of wxLANGUAGE_ENGLISH_UK + case wxLANGUAGE_ENGLISH_AUSTRALIA: + case wxLANGUAGE_ENGLISH_NEW_ZEALAND: + case wxLANGUAGE_ENGLISH_TRINIDAD: + case wxLANGUAGE_ENGLISH_CARIBBEAN: + case wxLANGUAGE_ENGLISH_JAMAICA: + case wxLANGUAGE_ENGLISH_BELIZE: + case wxLANGUAGE_ENGLISH_EIRE: + case wxLANGUAGE_ENGLISH_SOUTH_AFRICA: + case wxLANGUAGE_ENGLISH_ZIMBABWE: + case wxLANGUAGE_ENGLISH_BOTSWANA: + case wxLANGUAGE_ENGLISH_DENMARK: + return wxLANGUAGE_ENGLISH_UK; + + //variants of wxLANGUAGE_ENGLISH_US + case wxLANGUAGE_ENGLISH: + case wxLANGUAGE_ENGLISH_CANADA: + case wxLANGUAGE_ENGLISH_PHILIPPINES: + return wxLANGUAGE_ENGLISH_US; + //variants of wxLANGUAGE_GERMAN case wxLANGUAGE_GERMAN_AUSTRIAN: case wxLANGUAGE_GERMAN_BELGIUM: @@ -281,6 +324,7 @@ wxLanguage mapLanguageDialect(wxLanguage language) case wxLANGUAGE_NORWEGIAN_NYNORSK: return wxLANGUAGE_NORWEGIAN_BOKMAL; + //languages without variants: //case wxLANGUAGE_CZECH: //case wxLANGUAGE_DANISH: //case wxLANGUAGE_FINNISH: @@ -296,41 +340,6 @@ wxLanguage mapLanguageDialect(wxLanguage language) //case wxLANGUAGE_KOREAN: //case wxLANGUAGE_UKRAINIAN: //case wxLANGUAGE_CROATIAN: - - //variants of wxLANGUAGE_ARABIC - case wxLANGUAGE_ARABIC_ALGERIA: - case wxLANGUAGE_ARABIC_BAHRAIN: - case wxLANGUAGE_ARABIC_EGYPT: - case wxLANGUAGE_ARABIC_IRAQ: - case wxLANGUAGE_ARABIC_JORDAN: - case wxLANGUAGE_ARABIC_KUWAIT: - case wxLANGUAGE_ARABIC_LEBANON: - case wxLANGUAGE_ARABIC_LIBYA: - case wxLANGUAGE_ARABIC_MOROCCO: - case wxLANGUAGE_ARABIC_OMAN: - case wxLANGUAGE_ARABIC_QATAR: - case wxLANGUAGE_ARABIC_SAUDI_ARABIA: - case wxLANGUAGE_ARABIC_SUDAN: - case wxLANGUAGE_ARABIC_SYRIA: - case wxLANGUAGE_ARABIC_TUNISIA: - case wxLANGUAGE_ARABIC_UAE: - case wxLANGUAGE_ARABIC_YEMEN: - return wxLANGUAGE_ARABIC; - - //variants of wxLANGUAGE_ENGLISH_UK - case wxLANGUAGE_ENGLISH_AUSTRALIA: - case wxLANGUAGE_ENGLISH_NEW_ZEALAND: - case wxLANGUAGE_ENGLISH_TRINIDAD: - case wxLANGUAGE_ENGLISH_CARIBBEAN: - case wxLANGUAGE_ENGLISH_JAMAICA: - case wxLANGUAGE_ENGLISH_BELIZE: - case wxLANGUAGE_ENGLISH_EIRE: - case wxLANGUAGE_ENGLISH_SOUTH_AFRICA: - case wxLANGUAGE_ENGLISH_ZIMBABWE: - case wxLANGUAGE_ENGLISH_BOTSWANA: - case wxLANGUAGE_ENGLISH_DENMARK: - return wxLANGUAGE_ENGLISH_UK; - default: return language; } @@ -391,7 +400,7 @@ void zen::setLanguage(int language) //throw FileError else try { - zen::setTranslator(new FFSLocale(languageFile, static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, PluralForm::ParsingError + zen::setTranslator(new FFSLocale(languageFile, static_cast<wxLanguage>(language))); //throw lngfile::ParsingError, parse_plural::ParsingError } catch (lngfile::ParsingError& e) { @@ -400,7 +409,7 @@ void zen::setLanguage(int language) //throw FileError L"%y", numberTo<std::wstring>(e.row + 1)), L"%z", numberTo<std::wstring>(e.col + 1))); } - catch (PluralForm::ParsingError&) + catch (parse_plural::ParsingError&) { throw FileError(L"Invalid Plural Form"); } @@ -422,10 +431,3 @@ int zen::retrieveSystemLanguage() { return mapLanguageDialect(static_cast<wxLanguage>(wxLocale::GetSystemLanguage())); } - - -const std::vector<ExistingTranslations::Entry>& ExistingTranslations::get() -{ - static ExistingTranslations instance; - return instance.locMapping; -} diff --git a/lib/lock_holder.h b/lib/lock_holder.h index dd997853..d4fe27a9 100644 --- a/lib/lock_holder.h +++ b/lib/lock_holder.h @@ -46,7 +46,7 @@ public: try { - //lock file creation is synchronous and may block noticably for very slow devices (usb sticks, mapped cloud storages) + //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages) procCallback.forceUiRefresh(); //=> make sure the right folder name is shown on GUI during this time! lockHolder.insert(std::make_pair(dirnameFmt, DirLock(dirnameFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback))); } diff --git a/lib/parse_lng.h b/lib/parse_lng.h index 92564a1e..b5afe50c 100644 --- a/lib/parse_lng.h +++ b/lib/parse_lng.h @@ -51,19 +51,7 @@ void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap void parseHeader(const std::string& fileStream, TransHeader& header); //throw ParsingError class TranslationList; //unordered list of unique translation items -void generateLng(const TranslationList& in, const TransHeader& header, std::string& fileStream); - - - - - - - - - - - - +std::string generateLng(const TranslationList& in, const TransHeader& header); @@ -114,7 +102,7 @@ public: } private: - friend void generateLng(const TranslationList& in, const TransHeader& header, std::string& fileStream); + friend std::string generateLng(const TranslationList& in, const TransHeader& header); struct Item {virtual ~Item() {} }; struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} TranslationMap ::value_type value; }; @@ -218,7 +206,11 @@ private: class Scanner { public: - Scanner(const std::string& fileStream) : stream(fileStream), pos(stream.begin()) {} + Scanner(const std::string& fileStream) : stream(fileStream), pos(stream.begin()) + { + if (zen::startsWith(stream, zen::BYTE_ORDER_MARK_UTF8)) + pos += zen::strLength(zen::BYTE_ORDER_MARK_UTF8); + } Token nextToken() { @@ -228,19 +220,19 @@ public: if (pos == stream.end()) return Token(Token::TK_END); - for (KnownTokens::TokenMap::const_iterator i = KnownTokens::asList().begin(); i != KnownTokens::asList().end(); ++i) - if (startsWith(i->second)) + for (auto it = KnownTokens::asList().begin(); it != KnownTokens::asList().end(); ++it) + if (startsWith(it->second)) { - pos += i->second.size(); - return Token(i->first); + pos += it->second.size(); + return Token(it->first); } //rest must be "text" - std::string::const_iterator textBegin = pos; + std::string::const_iterator itBegin = pos; while (pos != stream.end() && !startsWithKnownTag()) pos = std::find(pos + 1, stream.end(), '<'); - std::string text(textBegin, pos); + std::string text(itBegin, pos); normalize(text); //remove whitespace from end ect. @@ -255,13 +247,8 @@ public: size_t posRow() const //current row beginning with 0 { //count line endings - size_t crSum = 0; //carriage returns - size_t nlSum = 0; //new lines - for (auto it = stream.begin(); it != pos; ++it) - if (*it == '\r') - ++crSum; - else if (*it == '\n') - ++nlSum; + const size_t crSum = std::count(stream.begin(), pos, '\r'); //carriage returns + const size_t nlSum = std::count(stream.begin(), pos, '\n'); //new lines assert(crSum == 0 || nlSum == 0 || crSum == nlSum); return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win } @@ -294,32 +281,15 @@ private: static void normalize(std::string& text) { - //remmove whitespace from end - while (!text.empty() && zen::isWhiteSpace(*text.rbegin())) - text.resize(text.size() - 1); - - //ensure c-style line breaks + zen::trim(text); //remmove whitespace from end //Delimiter: //---------- //Linux: 0xA \n //Mac: 0xD \r //Win: 0xD 0xA \r\n <- language files are in Windows format - if (text.find('\r') != std::string::npos) - { - std::string tmp; - for (std::string::const_iterator i = text.begin(); i != text.end(); ++i) - if (*i == '\r') - { - std::string::const_iterator next = i + 1; - if (next != text.end() && *next == '\n') - ++i; - tmp += '\n'; - } - else - tmp += *i; - text = tmp; - } + zen::replace(text, "\r\n", '\n'); // + zen::replace(text, "\r", '\n'); //ensure c-style line breaks } const std::string stream; @@ -334,7 +304,6 @@ public: void parse(TranslationMap& out, TranslationPluralMap& pluralOut, TransHeader& header) { - //header parseHeader(header); //items @@ -399,9 +368,7 @@ private: nextToken(); } consumeToken(Token::TK_TRG_END); - - if (!translation.empty()) //only add if translation is existing - out.insert(std::make_pair(original, translation)); + out.insert(std::make_pair(original, translation)); } void parsePlural(TranslationPluralMap& pluralOut, int formCount) @@ -437,20 +404,23 @@ private: throw ParsingError(scn.posRow(), scn.posCol()); consumeToken(Token::TK_TRG_END); - - if (!pluralList.empty()) //only add if translation is existing - pluralOut.insert(std::make_pair(SingularPluralPair(engSingular, engPlural), pluralList)); + pluralOut.insert(std::make_pair(SingularPluralPair(engSingular, engPlural), pluralList)); } void nextToken() { tk = scn.nextToken(); } const Token& token() const { return tk; } - void consumeToken(Token::Type t) + void consumeToken(Token::Type t) //throw ParsingError + { + expectToken(t); //throw ParsingError + nextToken(); + } + + void expectToken(Token::Type t) //throw ParsingError { if (token().type != t) throw ParsingError(scn.posRow(), scn.posCol()); - nextToken(); } Scanner scn; @@ -464,24 +434,22 @@ void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap out.clear(); pluralOut.clear(); - //skip UTF-8 Byte Ordering Mark - LngParser prs(zen::startsWith(fileStream, zen::BYTE_ORDER_MARK_UTF8) ? fileStream.substr(3) : fileStream); - prs.parse(out, pluralOut, header); + LngParser(fileStream).parse(out, pluralOut, header); } inline void parseHeader(const std::string& fileStream, TransHeader& header) //throw ParsingError { - //skip UTF-8 Byte Ordering Mark - LngParser prs(zen::startsWith(fileStream, zen::BYTE_ORDER_MARK_UTF8) ? fileStream.substr(3) : fileStream); - prs.parseHeader(header); + LngParser(fileStream).parseHeader(header); } inline void formatMultiLineText(std::string& text) { + assert(!zen::contains(text, "\r\n")); + if (text.find('\n') != std::string::npos) //multiple lines { if (*text.begin() != '\n') @@ -492,49 +460,46 @@ void formatMultiLineText(std::string& text) } -const std::string LB = "\n"; -const std::string TAB = "\t"; - - -void generateLng(const TranslationList& in, const TransHeader& header, std::string& fileStream) +std::string generateLng(const TranslationList& in, const TransHeader& header) { + std::string out; //header - fileStream += KnownTokens::text(Token::TK_HEADER_BEGIN) + LB; + out += KnownTokens::text(Token::TK_HEADER_BEGIN) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_LANG_NAME_BEGIN); - fileStream += header.languageName; - fileStream += KnownTokens::text(Token::TK_LANG_NAME_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_LANG_NAME_BEGIN); + out += header.languageName; + out += KnownTokens::text(Token::TK_LANG_NAME_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_TRANS_NAME_BEGIN); - fileStream += header.translatorName; - fileStream += KnownTokens::text(Token::TK_TRANS_NAME_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_TRANS_NAME_BEGIN); + out += header.translatorName; + out += KnownTokens::text(Token::TK_TRANS_NAME_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_LOCALE_NAME_BEGIN); - fileStream += header.localeName; - fileStream += KnownTokens::text(Token::TK_LOCALE_NAME_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_LOCALE_NAME_BEGIN); + out += header.localeName; + out += KnownTokens::text(Token::TK_LOCALE_NAME_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_FLAG_FILE_BEGIN); - fileStream += header.flagFile; - fileStream += KnownTokens::text(Token::TK_FLAG_FILE_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_FLAG_FILE_BEGIN); + out += header.flagFile; + out += KnownTokens::text(Token::TK_FLAG_FILE_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN); - fileStream += zen::numberTo<std::string>(header.pluralCount); - fileStream += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_PLURAL_COUNT_BEGIN); + out += zen::numberTo<std::string>(header.pluralCount); + out += KnownTokens::text(Token::TK_PLURAL_COUNT_END) + '\n'; - fileStream += TAB + KnownTokens::text(Token::TK_PLURAL_DEF_BEGIN); - fileStream += header.pluralDefinition; - fileStream += KnownTokens::text(Token::TK_PLURAL_DEF_END) + LB; + out += '\t' + KnownTokens::text(Token::TK_PLURAL_DEF_BEGIN); + out += header.pluralDefinition; + out += KnownTokens::text(Token::TK_PLURAL_DEF_END) + '\n'; - fileStream += KnownTokens::text(Token::TK_HEADER_END) + LB; + out += KnownTokens::text(Token::TK_HEADER_END) + '\n'; - fileStream += LB; + out += '\n'; //items - for (std::vector<TranslationList::Item*>::const_iterator i = in.sequence.begin(); i != in.sequence.end(); ++i) + for (auto it = in.sequence.begin(); it != in.sequence.end(); ++it) { - const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(*i); - const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem*>(*i); + const TranslationList::RegularItem* regular = dynamic_cast<const TranslationList::RegularItem*>(*it); + const TranslationList::PluralItem* plural = dynamic_cast<const TranslationList::PluralItem* >(*it); if (regular) { @@ -544,13 +509,13 @@ void generateLng(const TranslationList& in, const TransHeader& header, std::stri formatMultiLineText(original); formatMultiLineText(translation); - fileStream += KnownTokens::text(Token::TK_SRC_BEGIN); - fileStream += original; - fileStream += KnownTokens::text(Token::TK_SRC_END) + LB; + out += KnownTokens::text(Token::TK_SRC_BEGIN); + out += original; + out += KnownTokens::text(Token::TK_SRC_END) + '\n'; - fileStream += KnownTokens::text(Token::TK_TRG_BEGIN); - fileStream += translation; - fileStream += KnownTokens::text(Token::TK_TRG_END) + LB + LB; + out += KnownTokens::text(Token::TK_TRG_BEGIN); + out += translation; + out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; } else if (plural) @@ -562,34 +527,36 @@ void generateLng(const TranslationList& in, const TransHeader& header, std::stri formatMultiLineText(engSingular); formatMultiLineText(engPlural); - fileStream += KnownTokens::text(Token::TK_SRC_BEGIN) + LB; - fileStream += KnownTokens::text(Token::TK_PLURAL_BEGIN); - fileStream += engSingular; - fileStream += KnownTokens::text(Token::TK_PLURAL_END) + LB; - fileStream += KnownTokens::text(Token::TK_PLURAL_BEGIN); - fileStream += engPlural; - fileStream += KnownTokens::text(Token::TK_PLURAL_END) + LB; - fileStream += KnownTokens::text(Token::TK_SRC_END) + LB; + out += KnownTokens::text(Token::TK_SRC_BEGIN) + '\n'; + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += engSingular; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += engPlural; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; + out += KnownTokens::text(Token::TK_SRC_END) + '\n'; - fileStream += KnownTokens::text(Token::TK_TRG_BEGIN); - if (!forms.empty()) fileStream += LB; + out += KnownTokens::text(Token::TK_TRG_BEGIN); + if (!forms.empty()) out += '\n'; for (PluralForms::const_iterator j = forms.begin(); j != forms.end(); ++j) { std::string plForm = *j; formatMultiLineText(plForm); - fileStream += KnownTokens::text(Token::TK_PLURAL_BEGIN); - fileStream += plForm; - fileStream += KnownTokens::text(Token::TK_PLURAL_END) + LB; + out += KnownTokens::text(Token::TK_PLURAL_BEGIN); + out += plForm; + out += KnownTokens::text(Token::TK_PLURAL_END) + '\n'; } - fileStream += KnownTokens::text(Token::TK_TRG_END) + LB + LB; + out += KnownTokens::text(Token::TK_TRG_END) + '\n' + '\n'; } else { throw std::logic_error("that's what you get for brittle design ;)"); } } + assert(!zen::contains(out, "\r\n") && !zen::contains(out, "\r")); + return zen::replaceCpy(out, '\n', "\r\n"); //back to win line endings } } diff --git a/lib/parse_plural.h b/lib/parse_plural.h index 7af6809e..c3591881 100644 --- a/lib/parse_plural.h +++ b/lib/parse_plural.h @@ -7,11 +7,45 @@ #ifndef PARSE_PLURAL_H_INCLUDED #define PARSE_PLURAL_H_INCLUDED -#include <list> #include <memory> #include <functional> #include <zen/string_base.h> +namespace parse_plural +{ +//expression interface +struct Expression { virtual ~Expression() {} }; + +template <class T> +struct Expr : public Expression +{ + typedef T ValueType; + virtual ValueType eval() const = 0; +}; + + +class ParsingError {}; + +class PluralForm +{ +public: + PluralForm(const std::string& stream); //throw ParsingError + int getForm(int n) const { n_ = n ; return expr->eval(); } + +private: + std::shared_ptr<Expr<int>> expr; + mutable int n_; +}; + + + + + + + + +//--------------------------- implementation --------------------------- + //http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html //http://translate.sourceforge.net/wiki/l10n/pluralforms /* @@ -52,55 +86,57 @@ pm-expression: variable-number-n-expression constant-number-expression ( expression ) -*/ - -//expression interface -struct Expression { virtual ~Expression() {} }; +.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) +*/ -template <class T> -struct Expr : public Expression +namespace implementation { - typedef T ValueType; - virtual ValueType eval() const = 0; -}; - //specific binary expression based on STL function objects template <class StlOp> struct BinaryExp : public Expr<typename StlOp::result_type> { - typedef const Expr<typename StlOp::first_argument_type > ExpLhs; - typedef const Expr<typename StlOp::second_argument_type> ExpRhs; + typedef std::shared_ptr<Expr<typename StlOp::first_argument_type >> ExpLhs; + typedef std::shared_ptr<Expr<typename StlOp::second_argument_type>> ExpRhs; - BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) {} - virtual typename StlOp::result_type eval() const { return biop_(lhs_.eval(), rhs_.eval()); } - const ExpLhs& lhs_; - const ExpRhs& rhs_; + BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) { assert(lhs && rhs); } + virtual typename StlOp::result_type eval() const { return biop_(lhs_->eval(), rhs_->eval()); } +private: + ExpLhs lhs_; + ExpRhs rhs_; StlOp biop_; }; template <class StlOp> inline -BinaryExp<StlOp> makeBiExp(const Expression& lhs, const Expression& rhs, StlOp biop) //throw std::bad_cast +std::shared_ptr<BinaryExp<StlOp>> makeBiExp(const std::shared_ptr<Expression>& lhs, const std::shared_ptr<Expression>& rhs, StlOp biop) //throw ParsingError { - return BinaryExp<StlOp>(dynamic_cast<const Expr<typename StlOp::first_argument_type >&>(lhs), //throw std::bad_cast - dynamic_cast<const Expr<typename StlOp::second_argument_type>&>(rhs), biop); // + auto exLeft = std::dynamic_pointer_cast<Expr<typename StlOp::first_argument_type >>(lhs); + auto exRight = std::dynamic_pointer_cast<Expr<typename StlOp::second_argument_type>>(rhs); + if (!exLeft || !exRight) + throw ParsingError(); + return std::make_shared<BinaryExp<StlOp>>(exLeft, exRight, biop); } template <class T> -struct TernaryExp : public Expr<T> +struct ConditionalExp : public Expr<T> { - TernaryExp(const Expr<bool>& ifExp, const Expr<T>& thenExp, const Expr<T>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) {} - virtual typename Expr<T>::ValueType eval() const { return ifExp_.eval() ? thenExp_.eval() : elseExp_.eval(); } - const Expr<bool>& ifExp_; - const Expr<T>& thenExp_; - const Expr<T>& elseExp_; + ConditionalExp(const std::shared_ptr<Expr<bool>>& ifExp, + const std::shared_ptr<Expr<T>>& thenExp, + const std::shared_ptr<Expr<T>>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); } + + virtual typename Expr<T>::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } +private: + std::shared_ptr<Expr<bool>> ifExp_; + std::shared_ptr<Expr<T>> thenExp_; + std::shared_ptr<Expr<T>> elseExp_; }; struct ConstNumberExp : public Expr<int> { ConstNumberExp(int n) : n_(n) {} virtual int eval() const { return n_; } +private: int n_; }; @@ -108,296 +144,276 @@ struct VariableNumberNExp : public Expr<int> { VariableNumberNExp(int& n) : n_(n) {} virtual int eval() const { return n_; } +private: int& n_; }; +//------------------------------------------------------------------------------- -class PluralForm +struct Token { -public: - struct ParsingError {}; - - //.po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) - PluralForm(const std::string& phrase) : n_(0) + enum Type { - Parser(phrase, //in - expr, n_, dump); //out - } + TK_TERNARY_QUEST, + TK_TERNARY_COLON, + TK_OR, + TK_AND, + TK_EQUAL, + TK_NOT_EQUAL, + TK_LESS, + TK_LESS_EQUAL, + TK_GREATER, + TK_GREATER_EQUAL, + TK_MODULUS, + TK_VARIABLE_N, + TK_CONST_NUMBER, + TK_BRACKET_LEFT, + TK_BRACKET_RIGHT, + TK_END + }; - int getForm(int n) const { n_ = n ; return expr->eval(); } + Token(Type t) : type(t), number(0) {} + Token(int num) : type(TK_CONST_NUMBER), number(num) {} -private: - typedef std::list<std::shared_ptr<Expression> > DumpList; + Type type; + int number; //if type == TK_CONST_NUMBER +}; - struct Token +class Scanner +{ +public: + Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin()) { - enum Type - { - TK_TERNARY_QUEST, - TK_TERNARY_COLON, - TK_OR, - TK_AND, - TK_EQUAL, - TK_NOT_EQUAL, - TK_LESS, - TK_LESS_EQUAL, - TK_GREATER, - TK_GREATER_EQUAL, - TK_MODULUS, - TK_VARIABLE_N, - TK_CONST_NUMBER, - TK_BRACKET_LEFT, - TK_BRACKET_RIGHT, - TK_END - }; - - Token(Type t) : type(t), number(0) {} - Token(int num) : type(TK_CONST_NUMBER), number(num) {} - - Type type; - int number; //if type == TK_CONST_NUMBER - }; + tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST)); + tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON)); + tokens.push_back(std::make_pair("||", Token::TK_OR )); + tokens.push_back(std::make_pair("&&", Token::TK_AND )); + tokens.push_back(std::make_pair("==", Token::TK_EQUAL )); + tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL )); + tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL )); + tokens.push_back(std::make_pair("<" , Token::TK_LESS )); + tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL)); + tokens.push_back(std::make_pair(">" , Token::TK_GREATER )); + tokens.push_back(std::make_pair("%" , Token::TK_MODULUS )); + tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N )); + tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N )); + tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT )); + tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT)); + } - class Scanner + Token nextToken() { - public: - Scanner(const std::string& phrase) : stream(phrase), pos(stream.begin()) - { - tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST)); - tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON)); - tokens.push_back(std::make_pair("||", Token::TK_OR )); - tokens.push_back(std::make_pair("&&", Token::TK_AND )); - tokens.push_back(std::make_pair("==", Token::TK_EQUAL )); - tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL )); - tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL )); - tokens.push_back(std::make_pair("<" , Token::TK_LESS )); - tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL)); - tokens.push_back(std::make_pair(">" , Token::TK_GREATER )); - tokens.push_back(std::make_pair("%" , Token::TK_MODULUS )); - tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N )); - tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N )); - tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT )); - tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT)); - } - - Token nextToken() - { - //skip whitespace - pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); }); + //skip whitespace + pos = std::find_if(pos, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); }); - if (pos == stream.end()) - return Token::TK_END; + if (pos == stream_.end()) + return Token::TK_END; - for (auto iter = tokens.begin(); iter != tokens.end(); ++iter) - if (startsWith(iter->first)) - { - pos += iter->first.size(); - return Token(iter->second); - } - - auto digitEnd = std::find_if(pos, stream.end(), [](char c) { return !zen::isDigit(c); }); - - if (digitEnd != pos) + for (auto iter = tokens.begin(); iter != tokens.end(); ++iter) + if (startsWith(iter->first)) { - int number = zen::stringTo<int>(std::string(&*pos, digitEnd - pos)); - pos = digitEnd; - return number; + pos += iter->first.size(); + return Token(iter->second); } - throw ParsingError(); //unknown token - } + auto digitEnd = std::find_if(pos, stream_.end(), [](char c) { return !zen::isDigit(c); }); - private: - bool startsWith(const std::string& prefix) const + if (digitEnd != pos) { - if (stream.end() - pos < static_cast<ptrdiff_t>(prefix.size())) - return false; - return std::equal(prefix.begin(), prefix.end(), pos); + int number = zen::stringTo<int>(std::string(pos, digitEnd)); + pos = digitEnd; + return number; } - typedef std::vector<std::pair<std::string, Token::Type> > TokenList; - TokenList tokens; + throw ParsingError(); //unknown token + } - const std::string stream; - std::string::const_iterator pos; - }; +private: + bool startsWith(const std::string& prefix) const + { + if (stream_.end() - pos < static_cast<ptrdiff_t>(prefix.size())) + return false; + return std::equal(prefix.begin(), prefix.end(), pos); + } + typedef std::vector<std::pair<std::string, Token::Type> > TokenList; + TokenList tokens; + + const std::string stream_; + std::string::const_iterator pos; +}; + +//------------------------------------------------------------------------------- + +class Parser +{ +public: + Parser(const std::string& stream, int& n) : + scn(stream), + tk(scn.nextToken()), + n_(n) {} - class Parser + std::shared_ptr<Expr<int>> parse() //throw ParsingError; return value always bound! { - public: - Parser(const std::string& phrase, //in - const Expr<int>*& expr, int& n, PluralForm::DumpList& dump) : //out - scn(phrase), - tk(scn.nextToken()), - n_(n), - dump_(dump) - { - try - { - const Expression& e = parse(); //throw std::bad_cast, ParsingError - expr = &dynamic_cast<const Expr<int>&>(e); // - } - catch (std::bad_cast&) { throw ParsingError(); } + auto e = std::dynamic_pointer_cast<Expr<int>>(parseExpression()); //throw ParsingError + if (!e) + throw ParsingError(); + expectToken(Token::TK_END); + return e; + } - consumeToken(Token::TK_END); - } +private: + std::shared_ptr<Expression> parseExpression() { return parseConditional(); }//throw ParsingError - private: - void nextToken() { tk = scn.nextToken(); } - const Token& token() const { return tk; } + std::shared_ptr<Expression> parseConditional() //throw ParsingError + { + std::shared_ptr<Expression> e = parseLogicalOr(); - void consumeToken(Token::Type t) //throw ParsingError + if (token().type == Token::TK_TERNARY_QUEST) { - if (token().type != t) - throw ParsingError(); nextToken(); - } - const Expression& parse() { return parseConditional(); }; //throw std::bad_cast, ParsingError + auto ifExp = std::dynamic_pointer_cast<Expr<bool>>(e); + auto thenExp = std::dynamic_pointer_cast<Expr<int>>(parseExpression()); //associativity: <- - const Expression& parseConditional() - { - const Expression& e = parseLogicalOr(); + expectToken(Token::TK_TERNARY_COLON); + nextToken(); - if (token().type == Token::TK_TERNARY_QUEST) - { - nextToken(); - const Expression& thenEx = parse(); //associativity: <- - consumeToken(Token::TK_TERNARY_COLON); - const Expression& elseEx = parse(); // - - return manageObj(TernaryExp<int>(dynamic_cast<const Expr<bool>&>(e), // - dynamic_cast<const Expr<int>&>(thenEx), //throw std::bad_cast - dynamic_cast<const Expr<int>&>(elseEx))); // - } - return e; + auto elseExp = std::dynamic_pointer_cast<Expr<int>>(parseExpression()); // + if (!ifExp || !thenExp || !elseExp) + throw ParsingError(); + return std::make_shared<ConditionalExp<int>>(ifExp, thenExp, elseExp); } + return e; + } - const Expression& parseLogicalOr() + std::shared_ptr<Expression> parseLogicalOr() + { + std::shared_ptr<Expression> e = parseLogicalAnd(); + while (token().type == Token::TK_OR) //associativity: -> { - const Expression* e = &parseLogicalAnd(); - while (token().type == Token::TK_OR) //associativity: -> - { - nextToken(); - const Expression& rhs = parseLogicalAnd(); - e = &manageObj(makeBiExp(*e, rhs, std::logical_or<bool>())); //throw std::bad_cast - } - return *e; + nextToken(); + + std::shared_ptr<Expression> rhs = parseLogicalAnd(); + e = makeBiExp(e, rhs, std::logical_or<bool>()); //throw ParsingError } + return e; + } - const Expression& parseLogicalAnd() + std::shared_ptr<Expression> parseLogicalAnd() + { + std::shared_ptr<Expression> e = parseEquality(); + while (token().type == Token::TK_AND) //associativity: -> { - const Expression* e = &parseEquality(); - while (token().type == Token::TK_AND) //associativity: -> - { - nextToken(); - const Expression& rhs = parseEquality(); + nextToken(); + std::shared_ptr<Expression> rhs = parseEquality(); - e = &manageObj(makeBiExp(*e, rhs, std::logical_and<bool>())); //throw std::bad_cast - } - return *e; + e = makeBiExp(e, rhs, std::logical_and<bool>()); //throw ParsingError } + return e; + } - const Expression& parseEquality() - { - const Expression& e = parseRelational(); + std::shared_ptr<Expression> parseEquality() + { + std::shared_ptr<Expression> e = parseRelational(); - Token::Type t = token().type; - if (t == Token::TK_EQUAL || t == Token::TK_NOT_EQUAL) //associativity: n/a - { - nextToken(); - const Expression& rhs = parseRelational(); + Token::Type t = token().type; + if (t == Token::TK_EQUAL || //associativity: n/a + t == Token::TK_NOT_EQUAL) + { + nextToken(); + std::shared_ptr<Expression> rhs = parseRelational(); - if (t == Token::TK_EQUAL) return manageObj(makeBiExp(e, rhs, std::equal_to <int>())); //throw std::bad_cast - if (t == Token::TK_NOT_EQUAL) return manageObj(makeBiExp(e, rhs, std::not_equal_to<int>())); // - } - return e; + if (t == Token::TK_EQUAL) return makeBiExp(e, rhs, std::equal_to <int>()); //throw ParsingError + if (t == Token::TK_NOT_EQUAL) return makeBiExp(e, rhs, std::not_equal_to<int>()); // } + return e; + } - const Expression& parseRelational() - { - const Expression& e = parseMultiplicative(); + std::shared_ptr<Expression> parseRelational() + { + std::shared_ptr<Expression> e = parseMultiplicative(); - Token::Type t = token().type; - if (t == Token::TK_LESS || //associativity: n/a - t == Token::TK_LESS_EQUAL || - t == Token::TK_GREATER || - t == Token::TK_GREATER_EQUAL) - { - nextToken(); - const Expression& rhs = parseMultiplicative(); + Token::Type t = token().type; + if (t == Token::TK_LESS || //associativity: n/a + t == Token::TK_LESS_EQUAL || + t == Token::TK_GREATER || + t == Token::TK_GREATER_EQUAL) + { + nextToken(); + std::shared_ptr<Expression> rhs = parseMultiplicative(); - if (t == Token::TK_LESS) return manageObj(makeBiExp(e, rhs, std::less <int>())); // - if (t == Token::TK_LESS_EQUAL) return manageObj(makeBiExp(e, rhs, std::less_equal <int>())); //throw std::bad_cast - if (t == Token::TK_GREATER) return manageObj(makeBiExp(e, rhs, std::greater <int>())); // - if (t == Token::TK_GREATER_EQUAL) return manageObj(makeBiExp(e, rhs, std::greater_equal<int>())); // - } - return e; + if (t == Token::TK_LESS) return makeBiExp(e, rhs, std::less <int>()); // + if (t == Token::TK_LESS_EQUAL) return makeBiExp(e, rhs, std::less_equal <int>()); //throw ParsingError + if (t == Token::TK_GREATER) return makeBiExp(e, rhs, std::greater <int>()); // + if (t == Token::TK_GREATER_EQUAL) return makeBiExp(e, rhs, std::greater_equal<int>()); // } + return e; + } - const Expression& parseMultiplicative() - { - const Expression* e = &parsePrimary(); + std::shared_ptr<Expression> parseMultiplicative() + { + std::shared_ptr<Expression> e = parsePrimary(); - while (token().type == Token::TK_MODULUS) //associativity: -> - { - nextToken(); - const Expression& rhs = parsePrimary(); + while (token().type == Token::TK_MODULUS) //associativity: -> + { + nextToken(); + std::shared_ptr<Expression> rhs = parsePrimary(); - //"compile-time" check: n % 0 - if (auto literal = dynamic_cast<const ConstNumberExp*>(&rhs)) - if (literal->eval() == 0) - throw ParsingError(); + //"compile-time" check: n % 0 + if (auto literal = std::dynamic_pointer_cast<ConstNumberExp>(rhs)) + if (literal->eval() == 0) + throw ParsingError(); - e = &manageObj(makeBiExp(*e, rhs, std::modulus<int>())); //throw std::bad_cast - } - return *e; + e = makeBiExp(e, rhs, std::modulus<int>()); //throw ParsingError } + return e; + } - const Expression& parsePrimary() + std::shared_ptr<Expression> parsePrimary() + { + if (token().type == Token::TK_VARIABLE_N) { - if (token().type == Token::TK_VARIABLE_N) - { - nextToken(); - return manageObj(VariableNumberNExp(n_)); - } - else if (token().type == Token::TK_CONST_NUMBER) - { - const int number = token().number; - nextToken(); - return manageObj(ConstNumberExp(number)); - } - else if (token().type == Token::TK_BRACKET_LEFT) - { - nextToken(); - const Expression& e = parse(); - - consumeToken(Token::TK_BRACKET_RIGHT); - return e; - } - else - throw ParsingError(); + nextToken(); + return std::make_shared<VariableNumberNExp>(n_); } - - template <class T> - const T& manageObj(const T& obj) + else if (token().type == Token::TK_CONST_NUMBER) { - dump_.push_back(std::make_shared<T>(obj)); - return static_cast<T&>(*dump_.back()); + const int number = token().number; + nextToken(); + return std::make_shared<ConstNumberExp>(number); } + else if (token().type == Token::TK_BRACKET_LEFT) + { + nextToken(); + std::shared_ptr<Expression> e = parseExpression(); - Scanner scn; - Token tk; + expectToken(Token::TK_BRACKET_RIGHT); + nextToken(); + return e; + } + else + throw ParsingError(); + } - int& n_; - DumpList& dump_; //manage polymorphc object lifetimes - }; + void nextToken() { tk = scn.nextToken(); } + const Token& token() const { return tk; } - const Expr<int>* expr; - mutable int n_; + void expectToken(Token::Type t) //throw ParsingError + { + if (token().type != t) + throw ParsingError(); + } - PluralForm::DumpList dump; //manage polymorphc object lifetimes + Scanner scn; + Token tk; + int& n_; }; +} + + +inline +PluralForm::PluralForm(const std::string& stream) : expr(implementation::Parser(stream, n_).parse()) {} //throw ParsingError +} -#endif // PARSE_PLURAL_H_INCLUDED +#endif // PARSE_PLURAL_H_INCLUDED
\ No newline at end of file diff --git a/lib/perf_check.cpp b/lib/perf_check.cpp index c6a4e2d1..85a98910 100644 --- a/lib/perf_check.cpp +++ b/lib/perf_check.cpp @@ -98,34 +98,13 @@ wxString PerfCheck::getBytesPerSecond() const const double timeDelta = recordBack.first - recordFront.first; const double dataDelta = recordBack.second.data_ - recordFront.second.data_; - if (!numeric::isNull(timeDelta)) - if (dataDelta > 0) //may be negative if user cancels copying + if (!numeric::isNull(timeDelta) && dataDelta > 0) return filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec"); } return L"-"; //fallback } -wxString PerfCheck::getOverallBytesPerSecond() const //for all samples -{ - warn_static("WTF!? tihs considers window only!") - - if (!samples.empty()) - { - const auto& recordBack = *samples.rbegin(); - const auto& recordFront = *samples.begin(); - //----------------------------------------------------------------------------------------------- - const double timeDelta = recordBack.first - recordFront.first; - const double dataDelta = recordBack.second.data_ - recordFront.second.data_; - - if (!numeric::isNull(timeDelta)) - if (dataDelta > 0) //may be negative if user cancels copying - return zen::filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec"); - } - return L"-"; //fallback -} - - /* class for calculation of remaining time: ---------------------------------------- diff --git a/lib/perf_check.h b/lib/perf_check.h index b60c31c9..f314f842 100644 --- a/lib/perf_check.h +++ b/lib/perf_check.h @@ -21,7 +21,6 @@ public: wxString getRemainingTime(double dataRemaining) const; wxString getBytesPerSecond() const; //for window - wxString getOverallBytesPerSecond() const; //for all samples private: const long windowSizeRemTime; //unit: [ms] diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp index a11f841c..e38749f9 100644 --- a/lib/process_xml.cpp +++ b/lib/process_xml.cpp @@ -75,7 +75,7 @@ void setXmlType(XmlDoc& doc, XmlType type) //throw() wxString xmlAccess::getGlobalConfigFile() { - return toWx(zen::getConfigDir()) + wxT("GlobalSettings.xml"); + return toWx(zen::getConfigDir()) + L"GlobalSettings.xml"; } @@ -761,6 +761,64 @@ void writeStruc(const ColumnAttributeNavi& value, XmlElement& output) out.attribute("Width", value.offset_); out.attribute("Stretch", value.stretch_); } + + +template <> inline +bool readStruc(const XmlElement& input, ViewFilterDefault& value) +{ + XmlIn in(input); + + bool success = true; + auto readAttr = [&](XmlIn& elemIn, const char name[], bool& v) + { + if (!elemIn.attribute(name, v)) + success = false; + }; + + XmlIn catView = in["CategoryView"]; + readAttr(catView, "LeftOnly" , value.leftOnly); + readAttr(catView, "RightOnly" , value.rightOnly); + readAttr(catView, "LeftNewer" , value.leftNewer); + readAttr(catView, "RightNewer", value.rightNewer); + readAttr(catView, "Different" , value.different); + readAttr(catView, "Equal" , value.equal); + readAttr(catView, "Conflict" , value.conflict); + + XmlIn actView = in["ActionView"]; + readAttr(actView, "CreateLeft" , value.createLeft); + readAttr(actView, "CreateRight", value.createRight); + readAttr(actView, "UpdateLeft" , value.updateLeft); + readAttr(actView, "UpdateRight", value.updateRight); + readAttr(actView, "DeleteLeft" , value.deleteLeft); + readAttr(actView, "DeleteRight", value.deleteRight); + readAttr(actView, "DoNothing" , value.doNothing); + + return success; //[!] avoid short-circuit evaluation above +} + +template <> inline +void writeStruc(const ViewFilterDefault& value, XmlElement& output) +{ + XmlOut out(output); + + XmlOut catView = out["CategoryView"]; + catView.attribute("LeftOnly" , value.leftOnly); + catView.attribute("RightOnly" , value.rightOnly); + catView.attribute("LeftNewer" , value.leftNewer); + catView.attribute("RightNewer", value.rightNewer); + catView.attribute("Different" , value.different); + catView.attribute("Equal" , value.equal); + catView.attribute("Conflict" , value.conflict); + + XmlOut actView = out["ActionView"]; + actView.attribute("CreateLeft" , value.createLeft); + actView.attribute("CreateRight", value.createRight); + actView.attribute("UpdateLeft" , value.updateLeft); + actView.attribute("UpdateRight", value.updateRight); + actView.attribute("DeleteLeft" , value.deleteLeft); + actView.attribute("DeleteRight", value.deleteRight); + actView.attribute("DoNothing" , value.doNothing); +} } @@ -798,8 +856,12 @@ void readConfig(const XmlIn& in, SyncConfig& syncCfg) in["CustomDeletionFolder"](syncCfg.versioningDirectory);//obsolete name else in["VersioningFolder"](syncCfg.versioningDirectory); + warn_static("remove after migration?") - if (in["VersioningStyle"]) //new parameter + if (in["VersioningFolder"] && + in["VersioningFolder"].get()->getAttribute("Style", syncCfg.versioningStyle)) //new parameter, do not complain when missing + ; + else if (in["VersioningStyle"]) //obsolete name in["VersioningStyle"](syncCfg.versioningStyle); else syncCfg.versioningStyle = VER_STYLE_ADD_TIMESTAMP; //obsolete fallback @@ -921,7 +983,16 @@ void readConfig(const XmlIn& in, xmlAccess::XmlGuiConfig& config) inGuiCfg["HideExcluded"](config.hideExcludedItems); inGuiCfg["HandleError" ](config.handleError); - inGuiCfg["SyncPreviewActive"](config.showSyncAction); + + warn_static("remove after migration?") + if (inGuiCfg["SyncPreviewActive"]) //obsolete name + inGuiCfg["SyncPreviewActive"](config.showSyncAction); + else + { + std::string val; + if (inGuiCfg["MiddleGridView"](val)) //refactor into enum!? + config.showSyncAction = val == "Action"; + } } @@ -1003,7 +1074,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inWnd.attribute("Maximized", config.gui.isMaximized); XmlIn inManualDel = inWnd["ManualDeletion"]; - inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); + //inManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); inManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); inWnd["CaseSensitiveSearch" ](config.gui.textSearchRespectCase); @@ -1031,7 +1102,8 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inColRight(config.gui.columnAttribRight); //########################################################### - inWnd["Layout"](config.gui.guiPerspectiveLast); + inWnd["ViewFilterDefault"](config.gui.viewFilterDefault); + inWnd["Layout" ](config.gui.guiPerspectiveLast); //load config file history warn_static("remove after migration?") @@ -1140,7 +1212,7 @@ void writeConfig(const SyncConfig& syncCfg, XmlOut& out) out["DeletionPolicy" ](syncCfg.handleDeletion); out["VersioningFolder"](syncCfg.versioningDirectory); //out["VersioningFolder"].attribute("Limit", syncCfg.versionCountLimit); - out["VersioningStyle"](syncCfg.versioningStyle); + out["VersioningFolder"].attribute("Style", syncCfg.versioningStyle); } @@ -1234,9 +1306,9 @@ void writeConfig(const XmlGuiConfig& config, XmlOut& out) //write GUI specific config data XmlOut outGuiCfg = out["GuiConfig"]; - outGuiCfg["HideExcluded" ](config.hideExcludedItems); - outGuiCfg["HandleError" ](config.handleError); - outGuiCfg["SyncPreviewActive"](config.showSyncAction); + outGuiCfg["HideExcluded" ](config.hideExcludedItems); + outGuiCfg["HandleError" ](config.handleError); + outGuiCfg["MiddleGridView"](config.showSyncAction ? "Action" : "Category"); //refactor into enum!? } void writeConfig(const XmlBatchConfig& config, XmlOut& out) @@ -1296,7 +1368,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outWnd.attribute("Maximized", config.gui.isMaximized); XmlOut outManualDel = outWnd["ManualDeletion"]; - outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); + //outManualDel.attribute("DeleteOnBothSides", config.gui.deleteOnBothSides); outManualDel.attribute("UseRecycler" , config.gui.useRecyclerForManualDeletion); outWnd["CaseSensitiveSearch" ](config.gui.textSearchRespectCase); @@ -1323,7 +1395,8 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outColRight(config.gui.columnAttribRight); //########################################################### - outWnd["Layout"](config.gui.guiPerspectiveLast); + outWnd["ViewFilterDefault"](config.gui.viewFilterDefault); + outWnd["Layout" ](config.gui.guiPerspectiveLast); //load config file history outGui["LastUsedConfig"](config.gui.lastUsedConfigFiles); diff --git a/lib/process_xml.h b/lib/process_xml.h index 29237081..d0396d6e 100644 --- a/lib/process_xml.h +++ b/lib/process_xml.h @@ -112,6 +112,18 @@ enum FileIconSize }; +struct ViewFilterDefault +{ + ViewFilterDefault() : equal(false) + { + leftOnly = rightOnly = leftNewer = rightNewer = different = conflict = true; + createLeft = createRight = updateLeft = updateRight = deleteLeft = deleteRight = doNothing = true; + } + bool equal; + bool leftOnly, rightOnly, leftNewer, rightNewer, different, conflict; //category view + bool createLeft, createRight, updateLeft, updateRight, deleteLeft, deleteRight, doNothing; //action view +}; + wxString getGlobalConfigFile(); struct XmlGlobalSettings @@ -161,7 +173,7 @@ struct XmlGlobalSettings cfgFileHistMax(30), folderHistMax(15), onCompletionHistoryMax(8), - deleteOnBothSides(false), + //deleteOnBothSides(false), useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message #ifdef FFS_WIN textSearchRespectCase(false), @@ -216,7 +228,7 @@ struct XmlGlobalSettings std::vector<std::wstring> onCompletionHistory; size_t onCompletionHistoryMax; - bool deleteOnBothSides; + //bool deleteOnBothSides; bool useRecyclerForManualDeletion; bool textSearchRespectCase; @@ -225,6 +237,7 @@ struct XmlGlobalSettings long lastUpdateCheck; //time of last update check + ViewFilterDefault viewFilterDefault; wxString guiPerspectiveLast; //used by wxAuiManager } gui; diff --git a/lib/resources.cpp b/lib/resources.cpp index 7d46739e..2f7daeaf 100644 --- a/lib/resources.cpp +++ b/lib/resources.cpp @@ -87,8 +87,8 @@ GlobalResources::GlobalResources() const wxBitmap& GlobalResources::getImageInt(const wxString& imageName) const { auto it = bitmaps.find(!contains(imageName, L'.') ? //assume .png ending if nothing else specified - imageName + L".png" : - imageName); + imageName + L".png" : + imageName); if (it != bitmaps.end()) return it->second; else diff --git a/lib/status_handler.cpp b/lib/status_handler.cpp index c24c6f50..fd3b2d96 100644 --- a/lib/status_handler.cpp +++ b/lib/status_handler.cpp @@ -22,7 +22,7 @@ void zen::updateUiNow() namespace { -const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000; +const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL * ticksPerSec() / 1000; TickVal lastExec = getTicks(); }; diff --git a/lib/status_handler.h b/lib/status_handler.h index 93f9892c..ed496824 100644 --- a/lib/status_handler.h +++ b/lib/status_handler.h @@ -67,7 +67,7 @@ protected: refNumbers(numbersTotal_, currentPhase_) = std::make_pair(objectsTotal, dataTotal); } - virtual void updateProcessedData(int objectsDelta, Int64 dataDelta) { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods should NOT throw in order + virtual void updateProcessedData(int objectsDelta, Int64 dataDelta) { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods MUST NOT throw in order virtual void updateTotalData (int objectsDelta, Int64 dataDelta) { updateData(numbersTotal_ , objectsDelta, dataDelta); } //to properly allow undoing setting of statistics! virtual void requestUiRefresh() diff --git a/lib/versioning.cpp b/lib/versioning.cpp index d4b6e2b2..a72433cc 100644 --- a/lib/versioning.cpp +++ b/lib/versioning.cpp @@ -226,8 +226,26 @@ private: } -void FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError +bool FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError { + struct CallbackMoveFileImpl : public CallbackMoveDir + { + CallbackMoveFileImpl(CallbackMoveFile& callback) : callback_(callback) {} + private: + virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) {} + virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) {} + virtual void updateStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } + CallbackMoveFile& callback_; + } cb(callback); + + return revisionFileImpl(sourceFile, relativeName, cb); //throw FileError +} + + +bool FileVersioner::revisionFileImpl(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +{ + bool moveSuccessful = false; + moveItemToVersioning(sourceFile, //throw FileError relativeName, versioningDirectory_, @@ -235,26 +253,38 @@ void FileVersioner::revisionFile(const Zstring& sourceFile, const Zstring& relat versioningStyle_, [&](const Zstring& source, const Zstring& target) { + callback.onBeforeFileMove(source, target); //if we're called by revisionDirImpl() we know that "source" exists! + //when called by revisionFile(), "source" might not exist, however onBeforeFileMove() is not propagated in this case! + struct CopyCallbackImpl : public CallbackCopyFile { - CopyCallbackImpl(CallbackMoveFile& callback) : callback_(callback) {} + CopyCallbackImpl(CallbackMoveDir& callback) : callback_(callback) {} private: virtual void deleteTargetFile(const Zstring& targetFile) { assert(!somethingExists(targetFile)); } virtual void updateCopyStatus(Int64 bytesDelta) { callback_.updateStatus(bytesDelta); } - CallbackMoveFile& callback_; + CallbackMoveDir& callback_; } copyCallback(callback); - callback.onBeforeFileMove(source, target); moveFile(source, target, copyCallback); //throw FileError - callback.objectProcessed(); + moveSuccessful = true; }); + return moveSuccessful; +} + - //fileRelNames.push_back(relativeName); +void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError +{ + //no error situation if directory is not existing! manual deletion relies on it! + if (!somethingExists(sourceDir)) + return; //neither directory nor any other object (e.g. broken symlink) with that name existing + revisionDirImpl(sourceDir, relativeName, callback); //throw FileError } -void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveFile& callback) //throw FileError +void FileVersioner::revisionDirImpl(const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback) //throw FileError { + assert(somethingExists(sourceDir)); //[!] + //create target if (symlinkExists(sourceDir)) //on Linux there is just one type of symlinks, and since we do revision file symlinks, we should revision dir symlinks as well! { @@ -267,10 +297,7 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ { callback.onBeforeDirMove(source, target); moveDirSymlink(source, target); //throw FileError - callback.objectProcessed(); }); - - //fileRelNames.push_back(relativeName); } else { @@ -278,24 +305,15 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ assert(endsWith(sourceDir, relativeName)); //usually, yes, but we might relax this in the future const Zstring targetDir = appendSeparator(versioningDirectory_) + relativeName; - callback.onBeforeDirMove(sourceDir, targetDir); - //makeDirectory(targetDir); //FileError -> create only when needed in moveFileToVersioning(); avoids empty directories //traverse source directory one level std::vector<Zstring> fileList; //list of *short* names std::vector<Zstring> dirList; // - try { TraverseFilesOneLevel tol(fileList, dirList); //throw FileError traverseFolder(sourceDir, tol); // } - catch (FileError&) - { - if (!somethingExists(sourceDir)) //no source at all is not an error (however a file as source when a directory is expected, *is* an error!) - return; //object *not* processed - throw; - } const Zstring sourceDirPf = appendSeparator(sourceDir); const Zstring relnamePf = appendSeparator(relativeName); @@ -304,33 +322,23 @@ void FileVersioner::revisionDir(const Zstring& sourceDir, const Zstring& relativ std::for_each(fileList.begin(), fileList.end(), [&](const Zstring& shortname) { - revisionFile(sourceDirPf + shortname, //throw FileError - relnamePf + shortname, - callback); + revisionFileImpl(sourceDirPf + shortname, //throw FileError + relnamePf + shortname, + callback); }); //move items in subdirectories std::for_each(dirList.begin(), dirList.end(), [&](const Zstring& shortname) { - revisionDir(sourceDirPf + shortname, //throw FileError - relnamePf + shortname, - callback); + revisionDirImpl(sourceDirPf + shortname, //throw FileError + relnamePf + shortname, + callback); }); //delete source - struct RemoveCallbackImpl : public CallbackRemoveDir - { - RemoveCallbackImpl(CallbackMoveFile& callback) : callback_(callback) {} - private: - virtual void notifyFileDeletion(const Zstring& filename) { callback_.updateStatus(0); } - virtual void notifyDirDeletion (const Zstring& dirname ) { callback_.updateStatus(0); } - CallbackMoveFile& callback_; - } removeCallback(callback); - - removeDirectory(sourceDir, &removeCallback); //throw FileError - - callback.objectProcessed(); + callback.onBeforeDirMove(sourceDir, targetDir); + removeDirectory(sourceDir); //throw FileError } } diff --git a/lib/versioning.h b/lib/versioning.h index 3e0dd33c..faa96359 100644 --- a/lib/versioning.h +++ b/lib/versioning.h @@ -17,6 +17,7 @@ namespace zen { +struct CallbackMoveDir; struct CallbackMoveFile; //e.g. move C:\Source\subdir\Sample.txt -> D:\Revisions\subdir\Sample.txt 2012-05-15 131513.txt @@ -42,15 +43,18 @@ public: timeStamp_(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" { if (timeStamp_.size() != 17) //formatTime() returns empty string on error; unexpected length: e.g. problem in year 10000! - throw FileError(_("Failure to create time stamp for versioning:") + L" \'" + timeStamp_ + L"\'"); + throw FileError(_("Failure to create timestamp for versioning:") + L" \'" + timeStamp_ + L"\'"); } - void revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError - void revisionDir (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError + bool revisionFile(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveFile& callback); //throw FileError; return "false" if file is not existing + void revisionDir (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError //void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning! private: + bool revisionFileImpl(const Zstring& sourceFile, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + void revisionDirImpl (const Zstring& sourceDir, const Zstring& relativeName, CallbackMoveDir& callback); //throw FileError + const VersioningStyle versioningStyle_; const Zstring versioningDirectory_; const Zstring timeStamp_; @@ -59,22 +63,20 @@ private: }; -struct CallbackMoveFile +struct CallbackMoveFile //see CallbackCopyFile for limitations when throwing exceptions! { - virtual ~CallbackMoveFile() {} //see CallbackCopyFile for limitations when throwing exceptions! - - virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) = 0; //one call before each (planned) move - virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) = 0; // - virtual void objectProcessed() = 0; //one call after each completed move (count objects total) - - //called frequently if move has to revert to copy + delete: - virtual void updateStatus(Int64 bytesDelta) = 0; + virtual ~CallbackMoveFile() {} + virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete: }; +struct CallbackMoveDir //see CallbackCopyFile for limitations when throwing exceptions! +{ + virtual ~CallbackMoveDir() {} - - - + virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) = 0; //one call for each *existing* object! + virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) = 0; // + virtual void updateStatus(Int64 bytesDelta) = 0; //called frequently if move has to revert to copy + delete: +}; |