// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef DIFF_TOOL_H #define DIFF_TOOL_H #include "i18n.h" namespace STRING_MANAGER { const ucstring nl("\n"); struct TStringInfo { std::string Identifier; ucstring Text; ucstring Text2; mutable ucstring Comments; uint64 HashValue; }; struct TStringDiffContext { typedef std::vector::iterator iterator; const std::vector &Addition; std::vector &Reference; std::vector &Diff; TStringDiffContext(const std::vector &addition, std::vector &reference, std::vector &diff) : Addition(addition), Reference(reference), Diff(diff) { } }; struct TClause { std::string Identifier; ucstring Conditions; ucstring Text; ucstring Comments; uint64 HashValue; }; struct TPhrase { std::string Identifier; ucstring Parameters; mutable ucstring Comments; std::vector Clauses; uint64 HashValue; }; struct TPhraseDiffContext { typedef std::vector::iterator iterator; const std::vector &Addition; std::vector &Reference; std::vector &Diff; TPhraseDiffContext(const std::vector &addition, std::vector &reference, std::vector &diff) : Addition(addition), Reference(reference), Diff(diff) { } }; struct TWorksheet { typedef std::vector TRow; typedef std::vector TData; TData Data; uint ColCount; TWorksheet() : ColCount(0) { } std::vector::iterator begin() { return Data.begin(); } std::vector::iterator end() { return Data.end(); } std::vector::const_iterator begin() const { return Data.begin(); } std::vector::const_iterator end() const { return Data.end(); } void push_back(const TRow &row) { Data.push_back(row); } std::vector::iterator insert(std::vector::iterator pos, const TRow &value) { return Data.insert(pos, value); } std::vector::iterator erase(std::vector::iterator it) { return Data.erase(it); } TRow &back() { return Data.back(); } TRow &operator [] (uint index) { return Data[index]; } const TRow &operator [] (uint index) const { return Data[index]; } uint size() const { return (uint)Data.size(); } void insertColumn(uint colIndex) { nlassert(colIndex <= ColCount); for (uint i=0; i oldColIndex) { // the dst is after the src, no problem with index insertColumn(newColIndex); copyColumn(oldColIndex, newColIndex); eraseColumn(oldColIndex); } else { // the dst is before the src, need to take the column insertion into account insertColumn(newColIndex); copyColumn(oldColIndex+1, newColIndex); eraseColumn(oldColIndex+1); } } void setColCount(uint count) { if (count != ColCount) { for (uint i=0; ioperator[](colIndex) == colValue) { rowIndex = (uint)(first - Data.begin()); return true; } } return false; } void setData(uint rowIndex, uint colIndex, const ucstring &value) { nlassertex(rowIndex < Data.size(), ("TWorksheet::setData: bad row index: rowIndex(%u) is out of range (max=%u)", rowIndex, Data.size())); nlassertex(colIndex < ColCount, ("TWorksheet::setData: bad column index: colIndex(%u) is not less than ColCount(%u) ar rowIndex(%u)", colIndex, ColCount, rowIndex)); Data[rowIndex][colIndex] = value; } const ucstring &getData(uint rowIndex, uint colIndex) const { nlassertex(rowIndex < Data.size(), ("TWorksheet::getData: bad row index: rowIndex(%u) is out of range (max=%u)", rowIndex, Data.size())); nlassertex(colIndex < ColCount, ("TWorksheet::getData: bad column index: colIndex(%u) is not less than ColCount(%u) at rowIndex(%u)", colIndex, ColCount, rowIndex)); return Data[rowIndex][colIndex]; } void setData(uint rowIndex, const ucstring &colName, const ucstring &value) { nlassertex(rowIndex > 0, ("TWorksheet::setData: rowIndex(%u) must be greater then 0 !", rowIndex)); nlassertex(rowIndex < Data.size(), ("TWorksheet::setData: rowIndex(%u) is out of range (max=%u)", rowIndex, Data.size())); TWorksheet::TRow::iterator it = std::find(Data[0].begin(), Data[0].end(), ucstring(colName)); nlassertex(it != Data[0].end(), ("TWorksheet::setData: invalid colName: can't find the column named '%s' at row %u", colName.toString().c_str(), rowIndex)); Data[rowIndex][it - Data[0].begin()] = value; } const ucstring &getData(uint rowIndex, const ucstring &colName) const { nlassertex(rowIndex > 0, ("TWorksheet::getData: bad row index: rowIndex(%u) must be greater then 0 !", rowIndex)); nlassertex(rowIndex < Data.size(), ("TWorksheet::getData: bad row index: rowIndex(%u) is out of range (max=%u)", rowIndex, Data.size())); TWorksheet::TRow::const_iterator it = std::find(Data[0].begin(), Data[0].end(), ucstring(colName)); nlassertex(it != Data[0].end(), ("TWorksheet::getData: invalid colName: can't find the column named '%s' at row %u", colName.toString().c_str(), rowIndex)); return Data[rowIndex][it - Data[0].begin()]; } }; struct TGetWorksheetIdentifier { std::string operator()(const TWorksheet &container, uint index) const { return container.getData(index, 1).toString(); } }; struct TGetWorksheetHashValue { uint64 operator()(const TWorksheet &container, uint index) const { return NLMISC::CI18N::stringToHash(container.getData(index, ucstring("*HASH_VALUE")).toString()); } }; struct TTestWorksheetItem : public std::unary_function { ucstring Identifier; TTestWorksheetItem(const std::string &identifier) : Identifier(identifier) {} bool operator () (const TWorksheet::TRow &row) const { return row[1] == Identifier; } }; struct TWordsDiffContext { typedef TWorksheet::TData::iterator iterator; const TWorksheet &Addition; TWorksheet &Reference; TWorksheet &Diff; TWordsDiffContext(const TWorksheet &addition, TWorksheet &reference, TWorksheet &diff) : Addition(addition), Reference(reference), Diff(diff) { } }; template struct TGetIdentifier { std::string operator()(const std::vector &container, uint index) const { return container[index].Identifier; } }; template struct TGetHashValue { uint64 operator()(const std::vector &container, uint index) const { return container[index].HashValue; } }; template struct TTestItem : public std::unary_function { std::string Identifier; TTestItem(const std::string &identifier) : Identifier(identifier) {} bool operator () (const ItemType &item) const { return item.Identifier == Identifier; } }; /** * ItemType must have a property named Identifier that uniquely * identify each item. * ItemType must have a property named HashValue that is used * to determine the change between context.Addition and context.Reference vector. */ template , class GetHashValue = TGetHashValue, class TestItem = TTestItem > class CMakeDiff { public: struct IDiffCallback { virtual void onEquivalent(uint addIndex, uint refIndex, Context &context) = 0; virtual void onAdd(uint addIndex, uint refIndex, Context &context) = 0; virtual void onRemove(uint addIndex, uint refIndex, Context &context) = 0; virtual void onChanged(uint addIndex, uint refIndex, Context &context) = 0; virtual void onSwap(uint newIndex, uint refIndex, Context &context) = 0; }; void makeDiff(IDiffCallback *callback, Context &context, bool skipFirstRecord = false) { #ifdef NL_DEBUG // compile time checking // Context::iterator testIt; #endif GetIdentifier getIdentifier; GetHashValue getHashValue; // compare the context.Reference an context.Addition file, remove any equivalent strings. uint addCount, refCount; if (skipFirstRecord) { addCount = 1; refCount = 1; } else { addCount = 0; refCount=0; } while (addCount < context.Addition.size() || refCount < context.Reference.size()) { bool equal = true; if (addCount != context.Addition.size() && refCount != context.Reference.size()) { equal = getHashValue(context.Addition, addCount) == getHashValue(context.Reference, refCount); } // vector::iterator it; if (addCount == context.Addition.size() || ( !equal && find_if(context.Addition.begin(), context.Addition.end(), TestItem(getIdentifier(context.Reference, refCount))) == context.Addition.end() ) ) { // this can only be removal callback->onRemove(addCount, refCount, context); context.Reference.erase(context.Reference.begin()+refCount); // ++refCount; } else if (refCount == context.Reference.size() || ( !equal && find_if(context.Reference.begin(), context.Reference.end(), TestItem(getIdentifier(context.Addition, addCount))) == context.Reference.end() ) ) { // this can only be context.Addition callback->onAdd(addCount, refCount, context); context.Reference.insert(context.Reference.begin()+refCount, context.Addition[addCount]); ++refCount; ++addCount; } else if (getIdentifier(context.Addition, addCount) != getIdentifier(context.Reference, refCount)) { // swap two element. // Context::iterator it = find_if(context.Reference.begin(), context.Reference.end(), TestItem(getIdentifier(context.Addition, addCount))); // if (it == context.Reference.end()) if (find_if( context.Reference.begin(), context.Reference.end(), TestItem(getIdentifier(context.Addition, addCount))) == context.Reference.end()) { // context.Addition callback->onAdd(addCount, refCount, context); context.Reference.insert(context.Reference.begin()+refCount, context.Addition[addCount]); ++refCount; ++addCount; } else { // nlassert(it != context.Reference.begin()+refCount); uint index = (uint)(find_if(context.Reference.begin(), context.Reference.end(), TestItem(getIdentifier(context.Addition, addCount))) - context.Reference.begin()); // callback->onSwap(it - context.Reference.begin(), refCount, context); callback->onSwap(index, refCount, context); // std::swap(*it, context.Reference[refCount]); std::swap(context.Reference[index], context.Reference[refCount]); } } else if (getHashValue(context.Addition, addCount) != getHashValue(context.Reference, refCount)) { // changed element callback->onChanged(addCount, refCount, context); ++refCount; ++addCount; } else { // same entry callback->onEquivalent(addCount, refCount, context); addCount++; refCount++; } } } }; typedef CMakeDiff TStringDiff; typedef CMakeDiff TPhraseDiff; typedef CMakeDiff TWorkSheetDiff; uint64 makePhraseHash(const TPhrase &phrase); bool parseHashFromComment(const ucstring &comments, uint64 &hashValue); bool loadStringFile(const std::string filename, std::vector &stringInfos, bool forceRehash, ucchar openMark = '[', ucchar closeMark = ']', bool specialCase = false); ucstring prepareStringFile(const std::vector &strings, bool removeDiffComments, bool noDiffInfo = false); bool readPhraseFile(const std::string &filename, std::vector &phrases, bool forceRehash); bool readPhraseFileFromString(ucstring const& doc, const std::string &filename, std::vector &phrases, bool forceRehash); ucstring tabLines(uint nbTab, const ucstring &str); ucstring preparePhraseFile(const std::vector &phrases, bool removeDiffComments); bool loadExcelSheet(const std::string filename, TWorksheet &worksheet, bool checkUnique = true); bool readExcelSheet(const ucstring &text, TWorksheet &worksheet, bool checkUnique = true); void makeHashCode(TWorksheet &sheet, bool forceRehash); ucstring prepareExcelSheet(const TWorksheet &worksheet); } // namespace STRING_MANAGER #endif // DIFF_TOOL_H