2012-11-26 11:29:22 +00:00
|
|
|
#ifndef CSM_WOLRD_IDCOLLECTION_H
|
|
|
|
#define CSM_WOLRD_IDCOLLECTION_H
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cctype>
|
2012-11-29 17:56:28 +00:00
|
|
|
#include <stdexcept>
|
2012-12-03 12:56:02 +00:00
|
|
|
#include <functional>
|
2012-11-26 11:29:22 +00:00
|
|
|
|
|
|
|
#include <QVariant>
|
|
|
|
|
2013-02-07 11:52:01 +00:00
|
|
|
#include <components/esm/esmreader.hpp>
|
|
|
|
|
2013-01-09 19:51:52 +00:00
|
|
|
#include <components/misc/stringops.hpp>
|
2012-11-26 11:29:22 +00:00
|
|
|
|
2013-02-07 11:52:01 +00:00
|
|
|
#include "columnbase.hpp"
|
|
|
|
|
2012-11-26 11:29:22 +00:00
|
|
|
namespace CSMWorld
|
|
|
|
{
|
|
|
|
class IdCollectionBase
|
|
|
|
{
|
|
|
|
// not implemented
|
|
|
|
IdCollectionBase (const IdCollectionBase&);
|
|
|
|
IdCollectionBase& operator= (const IdCollectionBase&);
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
IdCollectionBase();
|
|
|
|
|
|
|
|
virtual ~IdCollectionBase();
|
|
|
|
|
|
|
|
virtual int getSize() const = 0;
|
|
|
|
|
|
|
|
virtual std::string getId (int index) const = 0;
|
|
|
|
|
2012-12-03 20:44:16 +00:00
|
|
|
virtual int getIndex (const std::string& id) const = 0;
|
|
|
|
|
2012-11-26 11:29:22 +00:00
|
|
|
virtual int getColumns() const = 0;
|
|
|
|
|
2012-12-13 13:53:16 +00:00
|
|
|
virtual const ColumnBase& getColumn (int column) const = 0;
|
2012-11-26 11:29:22 +00:00
|
|
|
|
|
|
|
virtual QVariant getData (int index, int column) const = 0;
|
2012-11-29 13:45:34 +00:00
|
|
|
|
|
|
|
virtual void setData (int index, int column, const QVariant& data) = 0;
|
|
|
|
|
2012-12-03 12:56:02 +00:00
|
|
|
virtual void merge() = 0;
|
|
|
|
///< Merge modified into base.
|
|
|
|
|
|
|
|
virtual void purge() = 0;
|
|
|
|
///< Remove records that are flagged as erased.
|
2012-12-03 20:44:16 +00:00
|
|
|
|
|
|
|
virtual void removeRows (int index, int count) = 0;
|
|
|
|
|
|
|
|
virtual void appendBlankRecord (const std::string& id) = 0;
|
2012-12-06 13:56:04 +00:00
|
|
|
|
|
|
|
virtual int searchId (const std::string& id) const = 0;
|
|
|
|
////< Search record with \a id.
|
|
|
|
/// \return index of record (if found) or -1 (not found)
|
|
|
|
|
|
|
|
virtual void replace (int index, const RecordBase& record) = 0;
|
|
|
|
///< If the record type does not match, an exception is thrown.
|
|
|
|
///
|
|
|
|
/// \attention \a record must not change the ID.
|
|
|
|
|
|
|
|
virtual void appendRecord (const RecordBase& record) = 0;
|
|
|
|
///< If the record type does not match, an exception is thrown.
|
|
|
|
|
|
|
|
virtual std::string getId (const RecordBase& record) const = 0;
|
|
|
|
///< Return ID for \a record.
|
|
|
|
///
|
2013-02-07 11:52:01 +00:00
|
|
|
/// \attention Throws an exception, if the type of \a record does not match.
|
2012-12-06 13:56:04 +00:00
|
|
|
|
|
|
|
virtual const RecordBase& getRecord (const std::string& id) const = 0;
|
2013-02-07 11:52:01 +00:00
|
|
|
|
|
|
|
virtual void load (ESM::ESMReader& reader, bool base) = 0;
|
2012-11-26 11:29:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
///< \brief Collection of ID-based records
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
class IdCollection : public IdCollectionBase
|
|
|
|
{
|
|
|
|
std::vector<Record<ESXRecordT> > mRecords;
|
|
|
|
std::map<std::string, int> mIndex;
|
|
|
|
std::vector<Column<ESXRecordT> *> mColumns;
|
|
|
|
|
|
|
|
// not implemented
|
|
|
|
IdCollection (const IdCollection&);
|
|
|
|
IdCollection& operator= (const IdCollection&);
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
IdCollection();
|
|
|
|
|
|
|
|
virtual ~IdCollection();
|
|
|
|
|
|
|
|
void add (const ESXRecordT& record);
|
|
|
|
///< Add a new record (modified)
|
|
|
|
|
|
|
|
virtual int getSize() const;
|
|
|
|
|
|
|
|
virtual std::string getId (int index) const;
|
|
|
|
|
2012-12-03 20:44:16 +00:00
|
|
|
virtual int getIndex (const std::string& id) const;
|
|
|
|
|
2012-11-26 11:29:22 +00:00
|
|
|
virtual int getColumns() const;
|
|
|
|
|
|
|
|
virtual QVariant getData (int index, int column) const;
|
|
|
|
|
2012-11-29 13:45:34 +00:00
|
|
|
virtual void setData (int index, int column, const QVariant& data);
|
|
|
|
|
2012-12-13 13:53:16 +00:00
|
|
|
virtual const ColumnBase& getColumn (int column) const;
|
2012-12-13 12:52:26 +00:00
|
|
|
|
2012-12-03 12:56:02 +00:00
|
|
|
virtual void merge();
|
|
|
|
///< Merge modified into base.
|
|
|
|
|
|
|
|
virtual void purge();
|
|
|
|
///< Remove records that are flagged as erased.
|
|
|
|
|
2012-12-03 20:44:16 +00:00
|
|
|
virtual void removeRows (int index, int count) ;
|
|
|
|
|
|
|
|
virtual void appendBlankRecord (const std::string& id);
|
|
|
|
|
2012-12-06 13:56:04 +00:00
|
|
|
virtual int searchId (const std::string& id) const;
|
|
|
|
////< Search record with \a id.
|
|
|
|
/// \return index of record (if found) or -1 (not found)
|
|
|
|
|
|
|
|
virtual void replace (int index, const RecordBase& record);
|
|
|
|
///< If the record type does not match, an exception is thrown.
|
|
|
|
///
|
|
|
|
/// \attention \a record must not change the ID.
|
|
|
|
|
|
|
|
virtual void appendRecord (const RecordBase& record);
|
|
|
|
///< If the record type does not match, an exception is thrown.
|
|
|
|
|
|
|
|
virtual std::string getId (const RecordBase& record) const;
|
|
|
|
///< Return ID for \a record.
|
|
|
|
///
|
|
|
|
/// \attention Throw san exception, if the type of \a record does not match.
|
|
|
|
|
|
|
|
virtual const RecordBase& getRecord (const std::string& id) const;
|
|
|
|
|
2013-02-07 11:52:01 +00:00
|
|
|
virtual void load (ESM::ESMReader& reader, bool base);
|
|
|
|
|
2012-11-29 13:45:34 +00:00
|
|
|
void addColumn (Column<ESXRecordT> *column);
|
2012-11-26 11:29:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
IdCollection<ESXRecordT>::IdCollection()
|
|
|
|
{}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
IdCollection<ESXRecordT>::~IdCollection()
|
|
|
|
{
|
|
|
|
for (typename std::vector<Column<ESXRecordT> *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
|
|
|
|
delete *iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::add (const ESXRecordT& record)
|
|
|
|
{
|
2013-01-09 19:51:52 +00:00
|
|
|
std::string id = Misc::StringUtils::lowerCase(record.mId);
|
2012-11-26 11:29:22 +00:00
|
|
|
|
|
|
|
std::map<std::string, int>::iterator iter = mIndex.find (id);
|
|
|
|
|
|
|
|
if (iter==mIndex.end())
|
|
|
|
{
|
|
|
|
Record<ESXRecordT> record2;
|
|
|
|
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
|
|
|
|
record2.mModified = record;
|
|
|
|
|
|
|
|
mRecords.push_back (record2);
|
2013-02-23 11:35:05 +00:00
|
|
|
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
|
2012-11-26 11:29:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mRecords[iter->second].setModified (record);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
int IdCollection<ESXRecordT>::getSize() const
|
|
|
|
{
|
|
|
|
return mRecords.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
std::string IdCollection<ESXRecordT>::getId (int index) const
|
|
|
|
{
|
|
|
|
return mRecords.at (index).get().mId;
|
|
|
|
}
|
|
|
|
|
2012-12-03 20:44:16 +00:00
|
|
|
template<typename ESXRecordT>
|
|
|
|
int IdCollection<ESXRecordT>::getIndex (const std::string& id) const
|
|
|
|
{
|
2012-12-06 13:56:04 +00:00
|
|
|
int index = searchId (id);
|
2012-12-03 20:44:16 +00:00
|
|
|
|
2012-12-06 13:56:04 +00:00
|
|
|
if (index==-1)
|
2012-12-03 20:44:16 +00:00
|
|
|
throw std::runtime_error ("invalid ID: " + id);
|
|
|
|
|
2012-12-06 13:56:04 +00:00
|
|
|
return index;
|
2012-12-03 20:44:16 +00:00
|
|
|
}
|
|
|
|
|
2012-11-26 11:29:22 +00:00
|
|
|
template<typename ESXRecordT>
|
|
|
|
int IdCollection<ESXRecordT>::getColumns() const
|
|
|
|
{
|
|
|
|
return mColumns.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
QVariant IdCollection<ESXRecordT>::getData (int index, int column) const
|
|
|
|
{
|
|
|
|
return mColumns.at (column)->get (mRecords.at (index));
|
|
|
|
}
|
|
|
|
|
2012-11-29 13:45:34 +00:00
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::setData (int index, int column, const QVariant& data)
|
|
|
|
{
|
|
|
|
return mColumns.at (column)->set (mRecords.at (index), data);
|
|
|
|
}
|
|
|
|
|
2012-11-26 11:29:22 +00:00
|
|
|
template<typename ESXRecordT>
|
2012-12-13 13:53:16 +00:00
|
|
|
const ColumnBase& IdCollection<ESXRecordT>::getColumn (int column) const
|
2012-12-13 12:52:26 +00:00
|
|
|
{
|
2012-12-13 13:53:16 +00:00
|
|
|
return *mColumns.at (column);
|
2012-12-13 12:52:26 +00:00
|
|
|
}
|
|
|
|
|
2012-11-26 11:29:22 +00:00
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::addColumn (Column<ESXRecordT> *column)
|
|
|
|
{
|
|
|
|
mColumns.push_back (column);
|
|
|
|
}
|
2012-12-03 12:56:02 +00:00
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::merge()
|
|
|
|
{
|
|
|
|
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
|
|
|
|
iter->merge();
|
|
|
|
|
|
|
|
purge();
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::purge()
|
|
|
|
{
|
|
|
|
mRecords.erase (std::remove_if (mRecords.begin(), mRecords.end(),
|
|
|
|
std::mem_fun_ref (&Record<ESXRecordT>::isErased) // I want lambda :(
|
|
|
|
), mRecords.end());
|
|
|
|
}
|
2012-12-03 20:44:16 +00:00
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::removeRows (int index, int count)
|
|
|
|
{
|
|
|
|
mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count);
|
|
|
|
|
|
|
|
typename std::map<std::string, int>::iterator iter = mIndex.begin();
|
|
|
|
|
|
|
|
while (iter!=mIndex.end())
|
|
|
|
{
|
|
|
|
if (iter->second>=index)
|
|
|
|
{
|
|
|
|
if (iter->second>=index+count)
|
|
|
|
{
|
|
|
|
iter->second -= count;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mIndex.erase (iter++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::appendBlankRecord (const std::string& id)
|
|
|
|
{
|
|
|
|
ESXRecordT record;
|
|
|
|
record.mId = id;
|
2012-12-03 21:16:02 +00:00
|
|
|
record.blank();
|
2012-12-03 20:44:16 +00:00
|
|
|
add (record);
|
|
|
|
}
|
2012-12-06 13:56:04 +00:00
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
int IdCollection<ESXRecordT>::searchId (const std::string& id) const
|
|
|
|
{
|
2013-01-09 19:51:52 +00:00
|
|
|
std::string id2 = Misc::StringUtils::lowerCase(id);
|
2012-12-06 13:56:04 +00:00
|
|
|
|
|
|
|
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
|
|
|
|
|
|
|
|
if (iter==mIndex.end())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return iter->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::replace (int index, const RecordBase& record)
|
|
|
|
{
|
|
|
|
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::appendRecord (const RecordBase& record)
|
|
|
|
{
|
|
|
|
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
|
2013-02-23 11:35:05 +00:00
|
|
|
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (getId (record)), mRecords.size()-1));
|
2012-12-06 13:56:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ESXRecordT>
|
|
|
|
std::string IdCollection<ESXRecordT>::getId (const RecordBase& record) const
|
|
|
|
{
|
|
|
|
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
|
|
|
|
return (record2.isModified() ? record2.mModified : record2.mBase).mId;
|
|
|
|
}
|
|
|
|
|
2013-02-07 11:52:01 +00:00
|
|
|
template<typename ESXRecordT>
|
|
|
|
void IdCollection<ESXRecordT>::load (ESM::ESMReader& reader, bool base)
|
|
|
|
{
|
|
|
|
std::string id = reader.getHNOString ("NAME");
|
|
|
|
|
|
|
|
int index = searchId (id);
|
|
|
|
|
2013-02-07 12:26:00 +00:00
|
|
|
if (reader.isNextSub ("DELE"))
|
2013-02-07 11:52:01 +00:00
|
|
|
{
|
2013-02-07 12:26:00 +00:00
|
|
|
reader.skipRecord();
|
2013-02-07 11:52:01 +00:00
|
|
|
|
2013-02-07 12:26:00 +00:00
|
|
|
if (index==-1)
|
|
|
|
{
|
|
|
|
// deleting a record that does not exist
|
|
|
|
|
|
|
|
// ignore it for now
|
|
|
|
|
|
|
|
/// \todo report the problem to the user
|
|
|
|
}
|
|
|
|
else if (base)
|
|
|
|
{
|
|
|
|
removeRows (index, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mRecords[index].mState = RecordBase::State_Deleted;
|
|
|
|
}
|
2013-02-07 11:52:01 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-02-07 12:26:00 +00:00
|
|
|
ESXRecordT record;
|
|
|
|
record.mId = id;
|
|
|
|
record.load (reader);
|
|
|
|
|
|
|
|
if (index==-1)
|
|
|
|
{
|
|
|
|
// new record
|
|
|
|
Record<ESXRecordT> record2;
|
|
|
|
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
|
|
|
(base ? record2.mBase : record2.mModified) = record;
|
2013-02-07 11:52:01 +00:00
|
|
|
|
2013-02-07 12:26:00 +00:00
|
|
|
appendRecord (record2);
|
|
|
|
}
|
2013-02-07 11:52:01 +00:00
|
|
|
else
|
2013-02-07 12:26:00 +00:00
|
|
|
{
|
|
|
|
// old record
|
|
|
|
Record<ESXRecordT>& record2 = mRecords[index];
|
|
|
|
|
|
|
|
if (base)
|
|
|
|
record2.mBase = record;
|
|
|
|
else
|
|
|
|
record2.setModified (record);
|
|
|
|
}
|
2013-02-07 11:52:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-06 13:56:04 +00:00
|
|
|
template<typename ESXRecordT>
|
|
|
|
const RecordBase& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
|
|
|
|
{
|
|
|
|
int index = getIndex (id);
|
|
|
|
return mRecords.at (index);
|
|
|
|
}
|
2012-11-26 11:29:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|