1 // Copyright (C) 2013 James Turner - zakalawe@mac.com
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Library General Public
5 // License as published by the Free Software Foundation; either
6 // version 2 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Library General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 #include <simgear/package/Package.hxx>
21 #include <boost/foreach.hpp>
22 #include <boost/algorithm/string/case_conv.hpp>
24 #include <simgear/debug/logstream.hxx>
25 #include <simgear/structure/exception.hxx>
27 #include <simgear/package/Catalog.hxx>
28 #include <simgear/package/Install.hxx>
29 #include <simgear/package/Root.hxx>
35 Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
38 initWithProps(aProps);
41 void Package::initWithProps(const SGPropertyNode* aProps)
43 m_props = const_cast<SGPropertyNode*>(aProps);
45 BOOST_FOREACH(const SGPropertyNode* c, aProps->getChildren("tag")) {
46 std::string t(c->getStringValue());
47 m_tags.insert(boost::to_lower_copy(t));
50 m_id = m_props->getStringValue("id");
53 void Package::updateFromProps(const SGPropertyNode* aProps)
56 initWithProps(aProps);
59 bool Package::matches(const SGPropertyNode* aFilter) const
61 int nChildren = aFilter->nChildren();
62 for (int i = 0; i < nChildren; i++) {
63 const SGPropertyNode* c = aFilter->getChild(i);
64 const std::string& filter_name = c->getNameString();
66 if (strutils::starts_with(filter_name, "rating-")) {
67 int minRating = c->getIntValue();
68 std::string rname = c->getName() + 7;
69 int ourRating = m_props->getChild("rating")->getIntValue(rname, 0);
70 if (ourRating < minRating) {
74 else if (filter_name == "tag") {
75 std::string tag(c->getStringValue());
77 if (m_tags.find(tag) == m_tags.end()) {
81 // substring search of name, description
82 else if (filter_name == "name") {
83 std::string n(c->getStringValue());
85 size_t pos = boost::to_lower_copy(name()).find(n);
86 if (pos == std::string::npos) {
90 else if (filter_name == "description") {
91 std::string n(c->getStringValue());
93 size_t pos = boost::to_lower_copy(description()).find(n);
94 if (pos == std::string::npos) {
98 else if (filter_name == "installed") {
99 if (isInstalled() != c->getBoolValue()) {
104 SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name);
105 } // of filter props iteration
110 bool Package::isInstalled() const
112 // anything to check for? look for a valid revision file?
113 return pathOnDisk().exists();
116 SGPath Package::pathOnDisk() const
118 SGPath p(m_catalog->installRoot());
119 p.append("Aircraft");
124 InstallRef Package::install()
126 InstallRef ins = existingInstall();
131 // start a new install
132 ins = new Install(this, pathOnDisk());
133 m_catalog->root()->scheduleToUpdate(ins);
135 _install_cb(this, ins);
140 InstallRef Package::existingInstall(const InstallCallback& cb) const
144 install = m_catalog->root()->existingInstallForPackage(const_cast<Package*>(this));
145 } catch (std::exception& ) {
151 _install_cb.push_back(cb);
154 cb(const_cast<Package*>(this), install);
160 std::string Package::id() const
165 std::string Package::qualifiedId() const
167 return m_catalog->id() + "." + id();
170 std::string Package::qualifiedVariantId(const unsigned int variantIndex) const
172 return m_catalog->id() + "." + variants()[variantIndex];
175 std::string Package::md5() const
177 return m_props->getStringValue("md5");
180 std::string Package::dirName() const
182 std::string r(m_props->getStringValue("dir"));
184 throw sg_exception("missing dir property on catalog package entry for " + m_id);
188 unsigned int Package::revision() const
194 return m_props->getIntValue("revision");
197 std::string Package::name() const
199 return m_props->getStringValue("name");
202 size_t Package::fileSizeBytes() const
204 return m_props->getIntValue("file-size-bytes");
207 std::string Package::description() const
209 return getLocalisedProp("description");
212 string_set Package::tags() const
217 SGPropertyNode* Package::properties() const
219 return m_props.ptr();
222 string_list Package::thumbnailUrls() const
229 BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
230 r.push_back(dl->getStringValue());
235 string_list Package::thumbnails() const
242 BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail-path")) {
243 r.push_back(dl->getStringValue());
248 string_list Package::downloadUrls() const
255 BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
256 r.push_back(dl->getStringValue());
261 std::string Package::getLocalisedProp(const std::string& aName) const
263 return getLocalisedString(m_props, aName.c_str());
266 std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
268 std::string locale = m_catalog->root()->getLocale();
269 if (aRoot->hasChild(locale)) {
270 const SGPropertyNode* localeRoot = aRoot->getChild(locale.c_str());
271 if (localeRoot->hasChild(aName)) {
272 return localeRoot->getStringValue(aName);
276 return aRoot->getStringValue(aName);
279 PackageList Package::dependencies() const
283 BOOST_FOREACH(SGPropertyNode* dep, m_props->getChildren("depends")) {
284 std::string depName = dep->getStringValue("id");
285 unsigned int rev = dep->getIntValue("revision", 0);
287 // prefer local hangar package if possible, in case someone does something
288 // silly with naming. Of course flightgear's aircraft search doesn't know
289 // about hangars, so names still need to be unique.
290 PackageRef depPkg = m_catalog->getPackageById(depName);
292 Root* rt = m_catalog->root();
293 depPkg = rt->getPackageById(depName);
295 throw sg_exception("Couldn't satisfy dependency of " + id() + " : " + depName);
299 if (depPkg->revision() < rev) {
300 throw sg_range_exception("Couldn't find suitable revision of " + depName);
303 // forbid recursive dependency graphs, we don't need that level
304 // of complexity for aircraft resources
305 assert(depPkg->dependencies() == PackageList());
307 result.push_back(depPkg);
313 string_list Package::variants() const
316 result.push_back(id());
318 BOOST_FOREACH(SGPropertyNode* var, m_props->getChildren("variant")) {
319 result.push_back(var->getStringValue("id"));
325 std::string Package::nameForVariant(const std::string& vid) const
331 BOOST_FOREACH(SGPropertyNode* var, m_props->getChildren("variant")) {
332 if (vid == var->getStringValue("id")) {
333 return var->getStringValue("name");
338 throw sg_exception("Unknow variant +" + vid + " in package " + id());
341 std::string Package::nameForVariant(const unsigned int vIndex) const
346 // offset by minus one to allow for index 0 being the primary
347 SGPropertyNode_ptr var = m_props->getChild("variant", vIndex - 1);
349 return var->getStringValue("name");
351 throw sg_exception("Unknow variant in package " + id());
355 } // of namespace pkg
357 } // of namespace simgear