TEST_F(SwdbTest, StoreRecords) { Swdb swdb("test.db"); EXPECT_TRUE(swdb.init()); uRecord pkg1 = swdb.urecord("pkgs"); pkg1->set("name", "xxx.noarch"); EXPECT_TRUE(pkg1->save()); uQuery q = swdb.uquery("pkgs"); q->filter("name", "xxx.noarch", Query::EQ); auto it = q->begin(); Record record = *it; string name; EXPECT_TRUE(record.get("name", name)); EXPECT_EQ(1, record.id()); EXPECT_EQ("xxx.noarch", name); ++it; EXPECT_TRUE(it == q->end()); }
// ToDo: seems to be auto-sorted in the dialog table display after insertion void PathgridEdgeListAdapter::addRow(Record<Pathgrid>& record, int position) const { Pathgrid pathgrid = record.get(); ESM::Pathgrid::EdgeList& edges = pathgrid.mEdges; // blank row ESM::Pathgrid::Edge edge; edge.mV0 = 0; edge.mV1 = 0; // NOTE: inserting a blank edge does not really make sense, perhaps this should be a // logic_error exception // // Currently the code assumes that the end user to know what he/she is doing. // e.g. Edges come in pairs, from points a->b and b->a edges.insert(edges.begin()+position, edge); record.setModified (pathgrid); }
// ToDo: detect duplicates in mEdges void PathgridEdgeListAdapter::setData(Record<Pathgrid>& record, const QVariant& value, int subRowIndex, int subColIndex) const { Pathgrid pathgrid = record.get(); if (subRowIndex < 0 || subRowIndex >= static_cast<int> (pathgrid.mEdges.size())) throw std::runtime_error ("index out of range"); ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; switch (subColIndex) { case 0: return; // return without saving case 1: edge.mV0 = value.toInt(); break; case 2: edge.mV1 = value.toInt(); break; default: throw std::runtime_error("Pathgrid edge subcolumn index out of range"); } pathgrid.mEdges[subRowIndex] = edge; record.setModified (pathgrid); }
virtual void addRow(Record<ESXRecordT>& record, int position) const { ESXRecordT magic = record.get(); std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList; // blank row ESM::ENAMstruct effect; effect.mEffectID = 0; effect.mSkill = -1; effect.mAttribute = -1; effect.mRange = 0; effect.mArea = 0; effect.mDuration = 0; effect.mMagnMin = 0; effect.mMagnMax = 0; effectsList.insert(effectsList.begin()+position, effect); record.setModified (magic); }
QVariant FactionReactionsAdapter::getData(const Record<ESM::Faction>& record, int subRowIndex, int subColIndex) const { ESM::Faction faction = record.get(); std::map<std::string, int>& reactions = faction.mReactions; if (subRowIndex < 0 || subRowIndex >= static_cast<int> (reactions.size())) throw std::runtime_error ("index out of range"); // FIXME: how to ensure that the map entries correspond to table indicies? // WARNING: Assumed that the table view has the same order as std::map std::map<std::string, int>::const_iterator iter = reactions.begin(); for(int i = 0; i < subRowIndex; ++i) ++iter; switch (subColIndex) { case 0: return QString((*iter).first.c_str()); case 1: return (*iter).second; default: throw std::runtime_error("Faction reactions subcolumn index out of range"); } }
void RegionSoundListAdapter::setData(Record<ESM::Region>& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESM::Region region = record.get(); std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList; if (subRowIndex < 0 || subRowIndex >= static_cast<int> (soundList.size())) throw std::runtime_error ("index out of range"); ESM::Region::SoundRef soundRef = soundList[subRowIndex]; switch (subColIndex) { case 0: soundRef.mSound.assign(value.toString().toUtf8().constData()); break; case 1: soundRef.mChance = static_cast<unsigned char>(value.toInt()); break; default: throw std::runtime_error("Region sounds subcolumn index out of range"); } region.mSoundList[subRowIndex] = soundRef; record.setModified (region); }
void FactionReactionsAdapter::setData(Record<ESM::Faction>& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESM::Faction faction = record.get(); std::map<std::string, int>& reactions = faction.mReactions; if (subRowIndex < 0 || subRowIndex >= static_cast<int> (reactions.size())) throw std::runtime_error ("index out of range"); // FIXME: how to ensure that the map entries correspond to table indicies? // WARNING: Assumed that the table view has the same order as std::map std::map<std::string, int>::iterator iter = reactions.begin(); for(int i = 0; i < subRowIndex; ++i) ++iter; std::string factionId = (*iter).first; int reaction = (*iter).second; switch (subColIndex) { case 0: { reactions.erase(iter); reactions.insert(std::make_pair(value.toString().toUtf8().constData(), reaction)); break; } case 1: { reactions[factionId] = value.toInt(); break; } default: throw std::runtime_error("Faction reactions subcolumn index out of range"); } record.setModified (faction); }
virtual void setData(Record<ESXRecordT>& record, const QVariant& value, int subRowIndex, int subColIndex) const { ESXRecordT raceOrBthSgn = record.get(); std::vector<std::string>& spells = raceOrBthSgn.mPowers.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int> (spells.size())) throw std::runtime_error ("index out of range"); std::string spell = spells[subRowIndex]; switch (subColIndex) { case 0: spell = value.toString().toUtf8().constData(); break; default: throw std::runtime_error("Spells subcolumn index out of range"); } raceOrBthSgn.mPowers.mList[subRowIndex] = spell; record.setModified (raceOrBthSgn); }
int FactionReactionsAdapter::getRowsCount(const Record<ESM::Faction>& record) const { return static_cast<int>(record.get().mReactions.size()); }
virtual QVariant getData(const Record<ESXRecordT>& record, int subRowIndex, int subColIndex) const { ESXRecordT magic = record.get(); std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList; if (subRowIndex < 0 || subRowIndex >= static_cast<int> (effectsList.size())) throw std::runtime_error ("index out of range"); ESM::ENAMstruct effect = effectsList[subRowIndex]; switch (subColIndex) { case 0: { if (effect.mEffectID >=0 && effect.mEffectID < ESM::MagicEffect::Length) return effect.mEffectID; else throw std::runtime_error("Magic effects ID unexpected value"); } case 1: { switch (effect.mEffectID) { case ESM::MagicEffect::DrainSkill: case ESM::MagicEffect::DamageSkill: case ESM::MagicEffect::RestoreSkill: case ESM::MagicEffect::FortifySkill: case ESM::MagicEffect::AbsorbSkill: return effect.mSkill; default: return QVariant(); } } case 2: { switch (effect.mEffectID) { case ESM::MagicEffect::DrainAttribute: case ESM::MagicEffect::DamageAttribute: case ESM::MagicEffect::RestoreAttribute: case ESM::MagicEffect::FortifyAttribute: case ESM::MagicEffect::AbsorbAttribute: return effect.mAttribute; default: return QVariant(); } } case 3: { if (effect.mRange >=0 && effect.mRange <=2) return effect.mRange; else throw std::runtime_error("Magic effects range unexpected value"); } case 4: return effect.mArea; case 5: return effect.mDuration; case 6: return effect.mMagnMin; case 7: return effect.mMagnMax; default: throw std::runtime_error("Magic Effects subcolumn index out of range"); } }
virtual QVariant get (const Record<ESXRecordT>& record) const { return record.get().mId.c_str(); }
virtual int getRowsCount(const Record<ESXRecordT>& record) const { return static_cast<int>(record.get().mEffects.mList.size()); }
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base, std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages) { Record<Cell> cell = mCells.getRecord (cellIndex); Cell& cell2 = base ? cell.mBase : cell.mModified; CellRef ref; ESM::MovedCellRef mref; bool isDeleted = false; // hack to initialise mindex while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref)) { // Keep mOriginalCell empty when in modified (as an indicator that the // original cell will always be equal the current cell). ref.mOriginalCell = base ? cell2.mId : ""; if (cell.get().isExterior()) { // ignoring moved references sub-record; instead calculate cell from coordinates std::pair<int, int> index = ref.getCellIndex(); std::ostringstream stream; stream << "#" << index.first << " " << index.second; ref.mCell = stream.str(); if (!base && // don't try to update base records mref.mRefNum.mIndex != 0) // MVRF tag found { // there is a requirement for a placeholder where the original object was // // see the forum discussions here for more details: // https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30 ref.mOriginalCell = cell2.mId; // It is not always possibe to ignore moved references sub-record and // calculate from coordinates. Some mods may place the ref in positions // outside normal bounds, resulting in non sensical cell id's. This often // happens if the moved ref was deleted. // // Use the target cell from the MVRF tag but if different output an error // message if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) { std::cerr << "The Position of moved ref " << ref.mRefID << " does not match the target cell" << std::endl; std::cerr << "Position: #" << index.first << " " << index.second <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; std::ostringstream stream; stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; ref.mCell = stream.str(); // overwrite } } } else ref.mCell = cell2.mId; // ignore content file number std::map<ESM::RefNum, std::string>::iterator iter = cache.begin(); for (; iter != cache.end(); ++iter) { if (ref.mRefNum.mIndex == iter->first.mIndex) break; } if (isDeleted) { if (iter==cache.end()) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, mCells.getId (cellIndex)); messages.add (id, "Attempt to delete a non-existing reference"); continue; } int index = getIndex (iter->second); Record<CellRef> record = getRecord (index); if (base) { removeRows (index, 1); cache.erase (iter); } else { record.mState = RecordBase::State_Deleted; setRecord (index, record); } continue; } if (iter==cache.end()) { // new reference ref.mId = getNewId(); Record<CellRef> record; record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record.mBase : record.mModified) = ref; appendRecord (record); cache.insert (std::make_pair (ref.mRefNum, ref.mId)); } else { // old reference -> merge ref.mId = iter->second; int index = getIndex (ref.mId); Record<CellRef> record = getRecord (index); record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; (base ? record.mBase : record.mModified) = ref; setRecord (index, record); } } }
virtual void set (Record<ESXRecordT>& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mValue.setFloat (data.toFloat()); record.setModified (record2); }
void CellListAdapter::setData(Record<CSMWorld::Cell>& record, const QVariant& value, int subRowIndex, int subColIndex) const { CSMWorld::Cell cell = record.get(); bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0; bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0; bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0; switch (subColIndex) { case 0: { if (value.toBool()) cell.mData.mFlags |= ESM::Cell::Interior; else cell.mData.mFlags &= ~ESM::Cell::Interior; break; } case 1: { if (isInterior && !behaveLikeExterior) cell.mAmbi.mAmbient = static_cast<int32_t>(value.toInt()); else return; // return without saving break; } case 2: { if (isInterior && !behaveLikeExterior) cell.mAmbi.mSunlight = static_cast<int32_t>(value.toInt()); else return; // return without saving break; } case 3: { if (isInterior && !behaveLikeExterior) cell.mAmbi.mFog = static_cast<int32_t>(value.toInt()); else return; // return without saving break; } case 4: { if (isInterior && !behaveLikeExterior) cell.mAmbi.mFogDensity = value.toFloat(); else return; // return without saving break; } case 5: { if (isInterior && !behaveLikeExterior && interiorWater) cell.mWater = value.toFloat(); else return; // return without saving break; } case 6: { if (!isInterior) cell.mMapColor = value.toInt(); else return; // return without saving break; } #if 0 // redundant since this flag is shown in the main table as "Interior Sky" // keep here for documenting the logic based on vanilla case 7: { if (isInterior) { if (value.toBool()) cell.mData.mFlags |= ESM::Cell::QuasiEx; else cell.mData.mFlags &= ~ESM::Cell::QuasiEx; } else return; // return without saving break; } #endif default: throw std::runtime_error("Cell subcolumn index out of range"); } record.setModified (cell); }
void InfoConditionAdapter::setData(Record<Info>& record, const QVariant& value, int subRowIndex, int subColIndex) const { Info info = record.get(); std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects; if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size())) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: { // See sInfoCondFunc in columns.cpp for the enum values switch (value.toInt()) { // FIXME: when these change the values of the other columns need to change // correspondingly (and automatically) case 1: { conditions[subRowIndex].mSelectRule[1] = '1'; // Function // default to "Rank Low" conditions[subRowIndex].mSelectRule[2] = '0'; conditions[subRowIndex].mSelectRule[3] = '0'; break; } case 2: conditions[subRowIndex].mSelectRule[1] = '2'; break; // Global case 3: conditions[subRowIndex].mSelectRule[1] = '3'; break; // Local case 4: conditions[subRowIndex].mSelectRule[1] = '4'; break; // Journal case 5: conditions[subRowIndex].mSelectRule[1] = '5'; break; // Item case 6: conditions[subRowIndex].mSelectRule[1] = '6'; break; // Dead case 7: conditions[subRowIndex].mSelectRule[1] = '7'; break; // Not ID case 8: conditions[subRowIndex].mSelectRule[1] = '8'; break; // Not Faction case 9: conditions[subRowIndex].mSelectRule[1] = '9'; break; // Not Class case 10: conditions[subRowIndex].mSelectRule[1] = 'A'; break; // Not Race case 11: conditions[subRowIndex].mSelectRule[1] = 'B'; break; // Not Cell case 12: conditions[subRowIndex].mSelectRule[1] = 'C'; break; // Not Local default: return; // return without saving } break; } case 1: { if (conditions[subRowIndex].mSelectRule[1] == '1') { std::map<const std::string, std::string>::const_iterator it = sEncToInfoFunc.begin(); for (;it != sEncToInfoFunc.end(); ++it) { if (it->second == value.toString().toUtf8().constData()) { std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 2); rule.append(it->first); // leave old values for undo (NOTE: may not be vanilla's behaviour) rule.append(conditions[subRowIndex].mSelectRule.substr(4)); conditions[subRowIndex].mSelectRule = rule; break; } } if (it == sEncToInfoFunc.end()) return; // return without saving; TODO: maybe log an error here } else { // FIXME: validate the string values before saving, based on the current function std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 5); conditions[subRowIndex].mSelectRule = rule.append(value.toString().toUtf8().constData()); } break; } case 2: { // See sInfoCondComp in columns.cpp for the enum values switch (value.toInt()) { case 0: conditions[subRowIndex].mSelectRule[4] = '1'; break; // != case 1: conditions[subRowIndex].mSelectRule[4] = '4'; break; // < case 2: conditions[subRowIndex].mSelectRule[4] = '5'; break; // <= case 3: conditions[subRowIndex].mSelectRule[4] = '0'; break; // = case 4: conditions[subRowIndex].mSelectRule[4] = '2'; break; // > case 5: conditions[subRowIndex].mSelectRule[4] = '3'; break; // >= default: return; // return without saving } break; } case 3: { switch (conditions[subRowIndex].mValue.getType()) { case ESM::VT_String: { conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData()); break; } case ESM::VT_Int: case ESM::VT_Short: case ESM::VT_Long: { conditions[subRowIndex].mValue.setInteger (value.toInt()); break; } case ESM::VT_Float: { conditions[subRowIndex].mValue.setFloat (value.toFloat()); break; } default: break; } break; } default: throw std::runtime_error("Info condition subcolumn index out of range"); } record.setModified (info); }
virtual QVariant get (const Record<ESXRecordT>& record) const { return QString::fromUtf8 (record.get().mId.c_str()); }
virtual void set (Record<ESXRecordT>& record, const QVariant& data) { ESXRecordT record2 = record.get(); record2.mValue.setType (static_cast<ESM::VarType> (data.toInt())); record.setModified (record2); }
virtual QVariant get (const Record<ESXRecordT>& record) const { return record.get().mData.mAttribute[mIndex]; }
virtual QVariant get (const Record<ESXRecordT>& record) const { return record.get().mData.mUseValue[mIndex]; }
virtual QVariant get (const Record<ESXRecordT>& record) const { return record.get().mData.mSpecialization; }
int RegionSoundListAdapter::getRowsCount(const Record<ESM::Region>& record) const { return static_cast<int>(record.get().mSoundList.size()); }
virtual QVariant get (const Record<ESXRecordT>& record) const { return static_cast<int> (record.get().mValue.getType()); }
QVariant InfoConditionAdapter::getData(const Record<Info>& record, int subRowIndex, int subColIndex) const { Info info = record.get(); std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects; if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size())) throw std::runtime_error ("index out of range"); switch (subColIndex) { case 0: { char condType = conditions[subRowIndex].mSelectRule[1]; switch (condType) { case '0': return 0; // blank space case '1': return 1; // Function case '2': return 2; // Global case '3': return 3; // Local case '4': return 4; // Journal case '5': return 5; // Item case '6': return 6; // Dead case '7': return 7; // Not ID case '8': return 8; // Not Factio case '9': return 9; // Not Class case 'A': return 10; // Not Race case 'B': return 11; // Not Cell case 'C': return 12; // Not Local default: return QVariant(); // TODO: log an error? } } case 1: { if (conditions[subRowIndex].mSelectRule[1] == '1') { // throws an exception if the encoding is not found return sEncToInfoFunc.at(conditions[subRowIndex].mSelectRule.substr(2, 2)).c_str(); } else return QString(conditions[subRowIndex].mSelectRule.substr(5).c_str()); } case 2: { char compType = conditions[subRowIndex].mSelectRule[4]; switch (compType) { case '0': return 3; // = case '1': return 0; // != case '2': return 4; // > case '3': return 5; // >= case '4': return 1; // < case '5': return 2; // <= default: return QVariant(); // TODO: log an error? } } case 3: { switch (conditions[subRowIndex].mValue.getType()) { case ESM::VT_String: { return QString::fromUtf8 (conditions[subRowIndex].mValue.getString().c_str()); } case ESM::VT_Int: case ESM::VT_Short: case ESM::VT_Long: { return conditions[subRowIndex].mValue.getInteger(); } case ESM::VT_Float: { return conditions[subRowIndex].mValue.getFloat(); } default: return QVariant(); } } default: throw std::runtime_error("Info condition subcolumn index out of range"); } }
int main (int argc, char** argv) { UnitTest t (21); // (blank) bool good = true; Record record; try {record = Record ("");} catch (std::string& e){t.diag (e); good = false;} t.notok (good, "Record::Record ('')"); // [] good = true; try {record = Record ("[]");} catch (std::string& e){t.diag (e); good = false;} t.notok (good, "Record::Record ('[]')"); // [name:value] good = true; try {record = Record ("[name:value]");} catch (std::string& e){t.diag (e); good = false;} t.ok (good, "Record::Record ('[name:value]')"); t.is (record.get ("name"), "value", "name=value"); // [name:"value"] good = true; try {record = Record ("[name:\"value\"]");} catch (std::string& e){t.diag (e); good = false;} t.ok (good, "Record::Record ('[name:\"value\"]')"); t.is (record.get ("name"), "value", "name=value"); // [name:"one two"] good = true; try {record = Record ("[name:\"one two\"]");} catch (std::string& e){t.diag (e); good = false;} t.ok (good, "Record::Record ('[name:\"one two\"]')"); t.is (record.get ("name"), "one two", "name=one two"); // [one:two three:four] good = true; try {record = Record ("[one:\"two\" three:\"four\"]");} catch (std::string& e){t.diag (e); good = false;} t.ok (good, "Record::Record ('[one:\"two\" three:\"four\"]')"); t.is (record.get ("one"), "two", "one=two"); t.is (record.get ("three"), "four", "three=four"); // Record::set record.clear (); record.set ("name", "value"); t.is (record.composeF4 (), "[name:\"value\"]\n", "Record::set"); // Record::has t.ok (record.has ("name"), "Record::has"); t.notok (record.has ("woof"), "Record::has not"); // Record::get_int record.set ("one", 1); t.is (record.composeF4 (), "[name:\"value\" one:\"1\"]\n", "Record::set"); t.is (record.get_int ("one"), 1, "Record::get_int"); // Record::get_ulong record.set ("two", "4294967295"); t.is (record.composeF4 (), "[name:\"value\" one:\"1\" two:\"4294967295\"]\n", "Record::set"); t.is ((size_t)record.get_ulong ("two"), (size_t)4294967295, "Record::get_ulong"); // Record::remove record.remove ("one"); record.remove ("two"); t.is (record.composeF4 (), "[name:\"value\"]\n", "Record::remove"); // Record::all std::vector <Att> all = record.all (); t.is (all.size (), (size_t)1, "Record::all size"); t.is (all[0].name (), "name", "Record::all[0].name ()"); return 0; }
int InfoConditionAdapter::getRowsCount(const Record<Info>& record) const { return static_cast<int>(record.get().mSelects.size()); }
int PathgridEdgeListAdapter::getRowsCount(const Record<Pathgrid>& record) const { return static_cast<int>(record.get().mEdges.size()); }
int RaceSkillsBonusAdapter::getRowsCount(const Record<ESM::Race>& record) const { // there are 7 skill bonuses return static_cast<int>(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0])); }
virtual QVariant get (const Record<ESXRecordT>& record) const { return record.get().mValue.getFloat(); }