Allow eg. Aircraft Center to add listeners to packages to be called if installation starts.
296 lines
7.7 KiB
C++
296 lines
7.7 KiB
C++
// Copyright (C) 2013 James Turner - zakalawe@mac.com
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Library General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library 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
|
|
// Library General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
//
|
|
|
|
#include <simgear/package/Package.hxx>
|
|
|
|
#include <cassert>
|
|
#include <boost/foreach.hpp>
|
|
#include <boost/algorithm/string/case_conv.hpp>
|
|
|
|
#include <simgear/debug/logstream.hxx>
|
|
#include <simgear/structure/exception.hxx>
|
|
|
|
#include <simgear/package/Catalog.hxx>
|
|
#include <simgear/package/Install.hxx>
|
|
#include <simgear/package/Root.hxx>
|
|
|
|
namespace simgear {
|
|
|
|
namespace pkg {
|
|
|
|
Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
|
|
m_catalog(aCatalog)
|
|
{
|
|
initWithProps(aProps);
|
|
}
|
|
|
|
void Package::initWithProps(const SGPropertyNode* aProps)
|
|
{
|
|
m_props = const_cast<SGPropertyNode*>(aProps);
|
|
// cache tag values
|
|
BOOST_FOREACH(const SGPropertyNode* c, aProps->getChildren("tag")) {
|
|
std::string t(c->getStringValue());
|
|
m_tags.insert(boost::to_lower_copy(t));
|
|
}
|
|
|
|
m_id = m_props->getStringValue("id");
|
|
}
|
|
|
|
void Package::updateFromProps(const SGPropertyNode* aProps)
|
|
{
|
|
m_tags.clear();
|
|
initWithProps(aProps);
|
|
}
|
|
|
|
bool Package::matches(const SGPropertyNode* aFilter) const
|
|
{
|
|
int nChildren = aFilter->nChildren();
|
|
for (int i = 0; i < nChildren; i++) {
|
|
const SGPropertyNode* c = aFilter->getChild(i);
|
|
const std::string& filter_name = c->getNameString();
|
|
|
|
if (strutils::starts_with(filter_name, "rating-")) {
|
|
int minRating = c->getIntValue();
|
|
std::string rname = c->getName() + 7;
|
|
int ourRating = m_props->getChild("rating")->getIntValue(rname, 0);
|
|
if (ourRating < minRating) {
|
|
return false;
|
|
}
|
|
}
|
|
else if (filter_name == "tag") {
|
|
std::string tag(c->getStringValue());
|
|
boost::to_lower(tag);
|
|
if (m_tags.find(tag) == m_tags.end()) {
|
|
return false;
|
|
}
|
|
}
|
|
// substring search of name, description
|
|
else if (filter_name == "name") {
|
|
std::string n(c->getStringValue());
|
|
boost::to_lower(n);
|
|
size_t pos = boost::to_lower_copy(name()).find(n);
|
|
if (pos == std::string::npos) {
|
|
return false;
|
|
}
|
|
}
|
|
else if (filter_name == "description") {
|
|
std::string n(c->getStringValue());
|
|
boost::to_lower(n);
|
|
size_t pos = boost::to_lower_copy(description()).find(n);
|
|
if (pos == std::string::npos) {
|
|
return false;
|
|
}
|
|
}
|
|
else if (filter_name == "installed") {
|
|
if (isInstalled() != c->getBoolValue()) {
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name);
|
|
} // of filter props iteration
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Package::isInstalled() const
|
|
{
|
|
// anything to check for? look for a valid revision file?
|
|
return pathOnDisk().exists();
|
|
}
|
|
|
|
SGPath Package::pathOnDisk() const
|
|
{
|
|
SGPath p(m_catalog->installRoot());
|
|
p.append("Aircraft");
|
|
p.append(id());
|
|
return p;
|
|
}
|
|
|
|
InstallRef Package::install()
|
|
{
|
|
InstallRef ins = existingInstall();
|
|
if (ins) {
|
|
return ins;
|
|
}
|
|
|
|
// start a new install
|
|
ins = new Install(this, pathOnDisk());
|
|
m_catalog->root()->scheduleToUpdate(ins);
|
|
|
|
_install_cb(this, ins);
|
|
|
|
return ins;
|
|
}
|
|
|
|
InstallRef Package::existingInstall(const InstallCallback& cb) const
|
|
{
|
|
InstallRef install = m_catalog->installForPackage(const_cast<Package*>(this));
|
|
|
|
if( cb )
|
|
{
|
|
_install_cb.push_back(cb);
|
|
|
|
if( install )
|
|
cb(const_cast<Package*>(this), install);
|
|
}
|
|
|
|
return install;
|
|
}
|
|
|
|
std::string Package::id() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
std::string Package::qualifiedId() const
|
|
{
|
|
return m_catalog->id() + "." + id();
|
|
}
|
|
|
|
std::string Package::md5() const
|
|
{
|
|
return m_props->getStringValue("md5");
|
|
}
|
|
|
|
unsigned int Package::revision() const
|
|
{
|
|
return m_props->getIntValue("revision");
|
|
}
|
|
|
|
std::string Package::name() const
|
|
{
|
|
return m_props->getStringValue("name");
|
|
}
|
|
|
|
size_t Package::fileSizeBytes() const
|
|
{
|
|
return m_props->getIntValue("file-size-bytes");
|
|
}
|
|
|
|
std::string Package::description() const
|
|
{
|
|
return getLocalisedProp("description");
|
|
}
|
|
|
|
SGPropertyNode* Package::properties() const
|
|
{
|
|
return m_props.ptr();
|
|
}
|
|
|
|
string_list Package::thumbnailUrls() const
|
|
{
|
|
string_list r;
|
|
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
|
|
r.push_back(dl->getStringValue());
|
|
}
|
|
return r;
|
|
}
|
|
|
|
string_list Package::downloadUrls() const
|
|
{
|
|
string_list r;
|
|
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
|
|
r.push_back(dl->getStringValue());
|
|
}
|
|
return r;
|
|
}
|
|
|
|
std::string Package::getLocalisedProp(const std::string& aName) const
|
|
{
|
|
return getLocalisedString(m_props, aName.c_str());
|
|
}
|
|
|
|
std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
|
|
{
|
|
std::string locale = m_catalog->root()->getLocale();
|
|
if (aRoot->hasChild(locale)) {
|
|
const SGPropertyNode* localeRoot = aRoot->getChild(locale.c_str());
|
|
if (localeRoot->hasChild(aName)) {
|
|
return localeRoot->getStringValue(aName);
|
|
}
|
|
}
|
|
|
|
return aRoot->getStringValue(aName);
|
|
}
|
|
|
|
PackageList Package::dependencies() const
|
|
{
|
|
PackageList result;
|
|
|
|
BOOST_FOREACH(SGPropertyNode* dep, m_props->getChildren("depends")) {
|
|
std::string depName = dep->getStringValue("package");
|
|
unsigned int rev = dep->getIntValue("revision", 0);
|
|
|
|
// prefer local hangar package if possible, in case someone does something
|
|
// silly with naming. Of course flightgear's aircraft search doesn't know
|
|
// about hanagrs, so names still need to be unique.
|
|
PackageRef depPkg = m_catalog->getPackageById(depName);
|
|
if (!depPkg) {
|
|
Root* rt = m_catalog->root();
|
|
depPkg = rt->getPackageById(depName);
|
|
if (!depPkg) {
|
|
throw sg_exception("Couldn't satisfy dependency of " + id() + " : " + depName);
|
|
}
|
|
}
|
|
|
|
if (depPkg->revision() < rev) {
|
|
throw sg_range_exception("Couldn't find suitable revision of " + depName);
|
|
}
|
|
|
|
// forbid recursive dependency graphs, we don't need that level
|
|
// of complexity for aircraft resources
|
|
assert(depPkg->dependencies() == PackageList());
|
|
|
|
result.push_back(depPkg);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
string_list Package::variants() const
|
|
{
|
|
string_list result;
|
|
result.push_back(id());
|
|
|
|
BOOST_FOREACH(SGPropertyNode* var, m_props->getChildren("variant")) {
|
|
result.push_back(var->getStringValue("id"));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::string Package::nameForVariant(const std::string& vid) const
|
|
{
|
|
if (vid == id()) {
|
|
return name();
|
|
}
|
|
|
|
BOOST_FOREACH(SGPropertyNode* var, m_props->getChildren("variant")) {
|
|
if (vid == var->getStringValue("id")) {
|
|
return var->getStringValue("name");
|
|
}
|
|
}
|
|
|
|
|
|
throw sg_exception("Unknow variant +" + vid + " in package " + id());
|
|
}
|
|
|
|
} // of namespace pkg
|
|
|
|
} // of namespace simgear
|