// 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