Implemented location metadata support.

Closes #287.
This commit is contained in:
Oliver Hamlet
2014-09-25 12:45:19 +01:00
parent 7bce28b08f
commit 7c1ed4b460
8 changed files with 167 additions and 4 deletions
+35 -1
View File
@@ -241,6 +241,18 @@ var pluginCardProto = Object.create(HTMLElement.prototype, {
data.crc = parseInt(data.crc, 16);
});
plugin.userlist.dirty = rowsData;
} else if (tables[j].id == 'locations') {
rowsData.forEach(function(data){
/* User metadata can only specify one version, but it should be a list. */
if (data.ver.length > 0) {
data.ver = [
data.ver
];
} else {
delete data.ver;
}
});
plugin.userlist.url = rowsData;
}
}
}
@@ -478,6 +490,28 @@ var pluginCardProto = Object.create(HTMLElement.prototype, {
});
}
} else if (tables[j].id == 'locations') {
if (loot.game.plugins[i].masterlist && loot.game.plugins[i].masterlist.url) {
loot.game.plugins[i].masterlist.url.forEach(function(location) {
var temp = location;
if (temp.ver) {
temp.ver = temp.ver[0];
}
var row = tables[j].addRow(temp);
tables[j].setReadOnly(row);
});
}
if (loot.game.plugins[i].userlist && loot.game.plugins[i].userlist.url) {
loot.game.plugins[i].userlist.url.forEach(function(location) {
var temp = location;
if (temp.ver) {
temp.ver = temp.ver[0];
}
tables[j].addRow(temp);
});
}
}
}
break;
@@ -909,7 +943,7 @@ var EditableTableProto = Object.create(HTMLTableElement.prototype, {
/* Data is an object with keys that match element class names. */
for (var key in tableData) {
var elems = row.getElementsByClassName(key);
if (elems.length == 1) {
if (tableData[key] != undefined && elems.length == 1) {
elems[0].value = tableData[key];
}
}
+16
View File
@@ -221,6 +221,7 @@
<span data-for="message">Messages</span>
<span data-for="tags">Bash Tags</span>
<span data-for="dirty">Dirty Info</span>
<span data-for="locations">Locations</span>
<table is="editable-table" id="loadAfter" data-template="fileRow">
<thead>
@@ -276,6 +277,15 @@
<tr><td colspan="4">Add new row...</td><td></td></tr>
</tbody>
</table>
<table is="editable-table" id="locations" class="hidden" data-template="locationRow">
<thead>
<tr><th>URL</th><th>Version</th><th></th></tr>
</thead>
<tbody>
<!-- Location rows go here. -->
<tr><td colspan="3">Add new row...</td><td></td></tr>
</tbody>
</table>
</div>
<hr>
<div id="buttons">
@@ -373,6 +383,12 @@
<td><input class="util" type="text" required>
<td class="fa fa-trash-o fa-fw">
</template>
<template id="locationRow">
<tr>
<td><input class="link" type="text" required>
<td><input class="ver" type="text">
<td class="fa fa-trash-o fa-fw">
</template>
<template id="gameRow">
<tr>
<td><input class="name" type="text" required>
+15
View File
@@ -143,6 +143,18 @@ namespace YAML {
return out;
}
Emitter& operator << (Emitter& out, const loot::Location& rhs) {
if (rhs.Versions().empty())
out << rhs.URL();
else {
out << BeginMap
<< Key << "link" << Value << rhs.URL()
<< Key << "ver" << Value << rhs.Versions()
<< EndMap;
}
return out;
}
Emitter& operator << (Emitter& out, const loot::Plugin& rhs) {
if (!rhs.HasNameOnly()) {
out << BeginMap
@@ -172,6 +184,9 @@ namespace YAML {
if (!rhs.DirtyInfo().empty())
out << Key << "dirty" << Value << rhs.DirtyInfo();
if (!rhs.Locations().empty())
out << Key << "url" << Value << rhs.Locations();
out << EndMap;
}
+4 -2
View File
@@ -20,7 +20,7 @@
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
*/
#ifndef __LOOT_GENERATORS__
#define __LOOT_GENERATORS__
@@ -37,7 +37,7 @@ namespace YAML {
template<class T, class Compare>
Emitter& operator << (Emitter& out, const std::set<T, Compare>& rhs) {
out << BeginSeq;
for (const auto &element: rhs) {
for (const auto &element : rhs) {
out << element;
}
out << EndSeq;
@@ -68,6 +68,8 @@ namespace YAML {
Emitter& operator << (Emitter& out, const loot::Tag& rhs);
Emitter& operator << (Emitter& out, const loot::Location& rhs);
Emitter& operator << (Emitter& out, const loot::Plugin& rhs);
}
+40 -1
View File
@@ -330,6 +330,24 @@ namespace loot {
return _name;
}
Location::Location() {}
Location::Location(const std::string& url) : _url(url) {}
Location::Location(const std::string& url, const std::vector<std::string>& versions) : _url(url), _versions(versions) {}
bool Location::operator < (const Location& rhs) const {
return boost::ilexicographical_compare(_url, rhs.URL());
}
std::string Location::URL() const {
return _url;
}
std::vector<std::string> Location::Versions() const {
return _versions;
}
Plugin::Plugin() : enabled(true), _isPriorityExplicit(false), priority(0), isMaster(false), crc(0), numOverrideRecords(0) {}
Plugin::Plugin(const std::string& n) : name(n), enabled(true), _isPriorityExplicit(false), priority(0), isMaster(false), crc(0), numOverrideRecords(0) {
//If the name passed ends in '.ghost', that should be trimmed.
@@ -465,6 +483,9 @@ namespace loot {
set<PluginDirtyInfo> dirtyInfo = plugin.DirtyInfo();
_dirtyInfo.insert(dirtyInfo.begin(), dirtyInfo.end());
set<Location> locations = plugin.Locations();
_locations.insert(locations.begin(), locations.end());
return;
}
@@ -511,6 +532,11 @@ namespace loot {
set_symmetric_difference(_dirtyInfo.begin(), _dirtyInfo.end(), dirtyInfo.begin(), dirtyInfo.end(), inserter(dirtDiff, dirtDiff.begin()));
p.DirtyInfo(dirtDiff);
set<Location> locations = plugin.Locations();
set<Location> locationsDiff;
set_symmetric_difference(_locations.begin(), _locations.end(), locations.begin(), locations.end(), inserter(locationsDiff, locationsDiff.begin()));
p.Locations(locationsDiff);
return p;
}
@@ -552,6 +578,11 @@ namespace loot {
set_difference(_dirtyInfo.begin(), _dirtyInfo.end(), dirtyInfo.begin(), dirtyInfo.end(), inserter(dirtDiff, dirtDiff.begin()));
p.DirtyInfo(dirtDiff);
set<Location> locations = plugin.Locations();
set<Location> locationsDiff;
set_difference(_locations.begin(), _locations.end(), locations.begin(), locations.end(), inserter(locationsDiff, locationsDiff.begin()));
p.Locations(locationsDiff);
return p;
}
@@ -591,6 +622,10 @@ namespace loot {
return _dirtyInfo;
}
std::set<Location> Plugin::Locations() const {
return _locations;
}
void Plugin::Name(const std::string& n) {
name = n;
}
@@ -631,6 +666,10 @@ namespace loot {
_dirtyInfo = dirtyInfo;
}
void Plugin::Locations(const std::set<Location>& locations) {
_locations = locations;
}
Plugin& Plugin::EvalAllConditions(Game& game, const unsigned int language) {
for (auto it = loadAfter.begin(); it != loadAfter.end();) {
if (!it->EvalCondition(game))
@@ -712,7 +751,7 @@ namespace loot {
}
bool Plugin::HasNameOnly() const {
return !IsPriorityExplicit() && loadAfter.empty() && requirements.empty() && incompatibilities.empty() && messages.empty() && tags.empty() && _dirtyInfo.empty();
return !IsPriorityExplicit() && loadAfter.empty() && requirements.empty() && incompatibilities.empty() && messages.empty() && tags.empty() && _dirtyInfo.empty() && _locations.empty();
}
bool Plugin::IsRegexPlugin() const {
+18
View File
@@ -161,6 +161,21 @@ namespace loot {
bool addTag;
};
class Location {
public:
Location();
Location(const std::string& url);
Location(const std::string& url, const std::vector<std::string>& versions);
bool operator < (const Location& rhs) const;
std::string URL() const;
std::vector<std::string> Versions() const;
private:
std::string _url;
std::vector<std::string> _versions;
};
class Plugin {
public:
Plugin();
@@ -190,6 +205,7 @@ namespace loot {
std::list<Message> Messages() const;
std::set<Tag> Tags() const;
std::set<PluginDirtyInfo> DirtyInfo() const;
std::set<Location> Locations() const;
const std::set<FormID>& FormIDs() const;
std::vector<std::string> Masters() const;
@@ -207,6 +223,7 @@ namespace loot {
void Messages(const std::list<Message>& messages);
void Tags(const std::set<Tag>& tags);
void DirtyInfo(const std::set<PluginDirtyInfo>& info);
void Locations(const std::set<Location>& locations);
Plugin& EvalAllConditions(Game& game, const unsigned int language);
void ParseAllConditions(Game& game) const;
@@ -239,6 +256,7 @@ namespace loot {
std::list<Message> messages;
std::set<Tag> tags;
std::set<PluginDirtyInfo> _dirtyInfo;
std::set<Location> _locations;
std::vector<std::string> masters;
std::set<FormID> formIDs;
+33
View File
@@ -293,6 +293,36 @@ namespace YAML {
}
};
template<>
struct convert < loot::Location > {
static Node encode(const loot::Location& rhs) {
Node node;
node["link"] = rhs.URL();
node["ver"] = rhs.Versions();
return node;
}
static bool decode(const Node& node, loot::Location& rhs) {
std::string url;
std::vector<std::string> versions;
if (node.IsMap()) {
if (node["link"])
url = node["link"].as<std::string>();
if (node["ver"])
versions = node["ver"].as<std::vector<std::string>>();
}
else if (node.IsScalar())
url = node.as<std::string>();
rhs = loot::Location(url, versions);
return true;
}
};
template<class T, class Compare>
struct convert < std::set<T, Compare> > {
static Node encode(const std::set<T, Compare>& rhs) {
@@ -350,6 +380,7 @@ namespace YAML {
node["msg"] = rhs.Messages();
node["tag"] = rhs.Tags();
node["dirty"] = rhs.DirtyInfo();
node["url"] = rhs.Locations();
return node;
}
@@ -380,6 +411,8 @@ namespace YAML {
rhs.Tags(node["tag"].as< std::set<loot::Tag> >());
if (node["dirty"])
rhs.DirtyInfo(node["dirty"].as< std::set<loot::PluginDirtyInfo> >());
if (node["url"])
rhs.Locations(node["url"].as< std::set<loot::Location> >());
return true;
}
+6
View File
@@ -484,8 +484,11 @@ namespace loot {
newUserlistEntry.Tags(pluginMetadata["userlist"]["tag"].as<set<Tag>>());
if (pluginMetadata["userlist"]["dirty"])
newUserlistEntry.DirtyInfo(pluginMetadata["userlist"]["dirty"].as<set<PluginDirtyInfo>>());
if (pluginMetadata["userlist"]["url"])
newUserlistEntry.Locations(pluginMetadata["userlist"]["url"].as<set<Location>>());
// For cleanliness, only data that does not duplicate masterlist and plugin data should be retained, so diff that.
BOOST_LOG_TRIVIAL(trace) << "Removing any user metadata that duplicates masterlist metadata.";
auto pluginIt = g_app_state.CurrentGame().plugins.find(boost::locale::to_lower(newUserlistEntry.Name()));
if (pluginIt != g_app_state.CurrentGame().plugins.end()) {
Plugin tempPlugin(pluginIt->second);
@@ -689,6 +692,7 @@ namespace loot {
pluginNode["masterlist"]["msg"] = mlistPlugin.Messages();
pluginNode["masterlist"]["tag"] = mlistPlugin.Tags();
pluginNode["masterlist"]["dirty"] = mlistPlugin.DirtyInfo();
pluginNode["masterlist"]["url"] = mlistPlugin.Locations();
}
if (!ulistPlugin.HasNameOnly()) {
@@ -700,6 +704,7 @@ namespace loot {
pluginNode["userlist"]["msg"] = ulistPlugin.Messages();
pluginNode["userlist"]["tag"] = ulistPlugin.Tags();
pluginNode["userlist"]["dirty"] = ulistPlugin.DirtyInfo();
pluginNode["userlist"]["url"] = ulistPlugin.Locations();
}
}
@@ -812,6 +817,7 @@ namespace loot {
pluginNode["masterlist"]["msg"] = mlistPlugin.Messages();
pluginNode["masterlist"]["tag"] = mlistPlugin.Tags();
pluginNode["masterlist"]["dirty"] = mlistPlugin.DirtyInfo();
pluginNode["masterlist"]["url"] = mlistPlugin.Locations();
}
// Now merge masterlist and userlist metadata and evaluate,