]> git.mxchange.org Git - simgear.git/blobdiff - simgear/package/Root.cxx
Expand package-system unit-tests.
[simgear.git] / simgear / package / Root.cxx
index 8d3b52a86a8b8ec9f8f26234597b2ccfa66787f1..785076530feed194e611c75e4edbe830d684decc 100644 (file)
@@ -34,7 +34,7 @@
 #include <simgear/package/Catalog.hxx>
 
 namespace simgear {
-    
+
 namespace pkg {
 
 typedef std::map<std::string, CatalogRef> CatalogDict;
@@ -50,20 +50,20 @@ public:
     m_owner(aOwner)
     {
     }
-    
+
 protected:
     virtual void gotBodyData(const char* s, int n)
     {
         m_buffer += std::string(s, n);
     }
-    
+
     virtual void onDone();
-    
+
 private:
     Root::RootPrivate* m_owner;
     std::string m_buffer;
 };
-    
+
 class Root::RootPrivate
 {
 public:
@@ -72,7 +72,7 @@ public:
         maxAgeSeconds(60 * 60 * 24)
     {
     }
-    
+
     void fireStartInstall(InstallRef install)
     {
         DelegateVec::const_iterator it;
@@ -80,7 +80,7 @@ public:
             (*it)->startInstall(install);
         }
     }
-    
+
     void fireInstallProgress(InstallRef install,
                              unsigned int aBytes, unsigned int aTotal)
     {
@@ -89,7 +89,7 @@ public:
             (*it)->installProgress(install, aBytes, aTotal);
         }
     }
-    
+
     void fireFinishInstall(InstallRef install, Delegate::StatusCode status)
     {
         DelegateVec::const_iterator it;
@@ -97,7 +97,7 @@ public:
             (*it)->finishInstall(install, status);
         }
     }
-    
+
     void fireRefreshStatus(CatalogRef catalog, Delegate::StatusCode status)
     {
         DelegateVec::const_iterator it;
@@ -105,8 +105,15 @@ public:
             (*it)->catalogRefreshed(catalog, status);
         }
     }
-    
-    
+
+    void firePackagesChanged()
+    {
+      DelegateVec::const_iterator it;
+      for (it = delegates.begin(); it != delegates.end(); ++it) {
+          (*it)->availablePackagesChanged();
+      }
+    }
+
     void thumbnailDownloadComplete(HTTP::Request_ptr request,
                                    Delegate::StatusCode status, const std::string& bytes)
     {
@@ -115,10 +122,10 @@ public:
             thumbnailCache[u] = bytes;
             fireDataForThumbnail(u, bytes);
         }
-        
+
         downloadNextPendingThumbnail();
     }
-    
+
     void fireDataForThumbnail(const std::string& aUrl, const std::string& bytes)
     {
         DelegateVec::const_iterator it;
@@ -127,48 +134,49 @@ public:
             (*it)->dataForThumbnail(aUrl, bytes.size(), data);
         }
     }
-    
+
     void downloadNextPendingThumbnail()
     {
         thumbnailDownloadRequest.clear();
         if (pendingThumbnails.empty()) {
             return;
         }
-        
+
         std::string u = pendingThumbnails.front();
         pendingThumbnails.pop_front();
         thumbnailDownloadRequest = new Root::ThumbnailDownloader(this, u);
-        
+
         if (http) {
             http->makeRequest(thumbnailDownloadRequest);
         } else {
             httpPendingRequests.push_back(thumbnailDownloadRequest);
         }
     }
-    
+
     DelegateVec delegates;
-    
+
     SGPath path;
     std::string locale;
     HTTP::Client* http;
     CatalogDict catalogs;
+    CatalogList disabledCatalogs;
     unsigned int maxAgeSeconds;
     std::string version;
-    
+
     std::set<CatalogRef> refreshing;
     typedef std::deque<InstallRef> UpdateDeque;
     UpdateDeque updateDeque;
     std::deque<HTTP::Request_ptr> httpPendingRequests;
-    
+
     HTTP::Request_ptr thumbnailDownloadRequest;
     StringDeque pendingThumbnails;
     MemThumbnailCache thumbnailCache;
-    
+
     typedef std::map<PackageRef, InstallRef> InstallCache;
     InstallCache m_installs;
 };
-    
-    
+
+
 void Root::ThumbnailDownloader::onDone()
 {
     if (responseCode() != 200) {
@@ -176,23 +184,23 @@ void Root::ThumbnailDownloader::onDone()
         m_owner->thumbnailDownloadComplete(this, Delegate::FAIL_DOWNLOAD, std::string());
         return;
     }
-    
+
     m_owner->thumbnailDownloadComplete(this, Delegate::STATUS_SUCCESS, m_buffer);
     //time(&m_owner->m_retrievedTime);
     //m_owner->writeTimestamp();
     //m_owner->refreshComplete(Delegate::STATUS_REFRESHED);
 }
-    
+
 SGPath Root::path() const
 {
     return d->path;
 }
-    
+
 void Root::setMaxAgeSeconds(unsigned int seconds)
 {
     d->maxAgeSeconds = seconds;
 }
-    
+
 unsigned int Root::maxAgeSeconds() const
 {
     return d->maxAgeSeconds;
@@ -214,10 +222,24 @@ void Root::makeHTTPRequest(HTTP::Request *req)
         d->http->makeRequest(req);
         return;
     }
-    
+
     d->httpPendingRequests.push_back(req);
 }
-    
+
+void Root::cancelHTTPRequest(HTTP::Request *req, const std::string &reason)
+{
+    if (d->http) {
+        d->http->cancelRequest(req, reason);
+    }
+
+    std::deque<HTTP::Request_ptr>::iterator it = std::find(d->httpPendingRequests.begin(),
+                                                           d->httpPendingRequests.end(),
+                                                           req);
+    if (it != d->httpPendingRequests.end()) {
+        d->httpPendingRequests.erase(it);
+    }
+}
+
 Root::Root(const SGPath& aPath, const std::string& aVersion) :
     d(new RootPrivate)
 {
@@ -226,50 +248,56 @@ Root::Root(const SGPath& aPath, const std::string& aVersion) :
     if (getenv("LOCALE")) {
         d->locale = getenv("LOCALE");
     }
-    
+
     Dir dir(aPath);
     if (!dir.exists()) {
         dir.create(0755);
         return;
     }
-    
-    BOOST_FOREACH(SGPath c, dir.children(Dir::TYPE_DIR)) {
+
+    BOOST_FOREACH(SGPath c, dir.children(Dir::TYPE_DIR | Dir::NO_DOT_OR_DOTDOT)) {
         CatalogRef cat = Catalog::createFromPath(this, c);
         if (cat) {
-           d->catalogs[cat->id()] = cat;
+            if (cat->status() == Delegate::STATUS_SUCCESS) {
+                d->catalogs[cat->id()] = cat;
+            } else {
+                // catalog has problems, such as needing an update
+                // keep it out of the main collection for now
+                d->disabledCatalogs.push_back(cat);
+            }
         }
     } // of child directories iteration
 }
 
 Root::~Root()
 {
-    
+
 }
 
 int Root::catalogVersion() const
 {
     return 4;
 }
-    
+
 std::string Root::applicationVersion() const
 {
     return d->version;
 }
-    
+
 CatalogRef Root::getCatalogById(const std::string& aId) const
 {
     CatalogDict::const_iterator it = d->catalogs.find(aId);
     if (it == d->catalogs.end()) {
         return NULL;
     }
-    
+
     return it->second;
 }
 
 PackageRef Root::getPackageById(const std::string& aName) const
 {
     size_t lastDot = aName.rfind('.');
-    
+
     PackageRef pkg = NULL;
     if (lastDot == std::string::npos) {
         // naked package ID
@@ -280,17 +308,17 @@ PackageRef Root::getPackageById(const std::string& aName) const
                 return pkg;
             }
         }
-        
+
         return NULL;
     }
-    
+
     std::string catalogId = aName.substr(0, lastDot);
-    std::string id = aName.substr(lastDot + 1);    
+    std::string id = aName.substr(lastDot + 1);
     CatalogRef catalog = getCatalogById(catalogId);
     if (!catalog) {
         return NULL;
     }
-            
+
     return catalog->getPackageById(id);
 }
 
@@ -301,7 +329,7 @@ CatalogList Root::catalogs() const
     for (; it != d->catalogs.end(); ++it) {
         r.push_back(it->second);
     }
-    
+
     return r;
 }
 
@@ -309,27 +337,27 @@ PackageList
 Root::allPackages() const
 {
     PackageList r;
-    
+
     CatalogDict::const_iterator it = d->catalogs.begin();
     for (; it != d->catalogs.end(); ++it) {
         const PackageList& r2(it->second->packages());
         r.insert(r.end(), r2.begin(), r2.end());
     }
-    
+
     return r;
 }
-    
+
 PackageList
 Root::packagesMatching(const SGPropertyNode* aFilter) const
 {
     PackageList r;
-    
+
     CatalogDict::const_iterator it = d->catalogs.begin();
     for (; it != d->catalogs.end(); ++it) {
         PackageList r2(it->second->packagesMatching(aFilter));
         r.insert(r.end(), r2.begin(), r2.end());
     }
-    
+
     return r;
 }
 
@@ -337,27 +365,38 @@ PackageList
 Root::packagesNeedingUpdate() const
 {
     PackageList r;
-    
+
     CatalogDict::const_iterator it = d->catalogs.begin();
     for (; it != d->catalogs.end(); ++it) {
         PackageList r2(it->second->packagesNeedingUpdate());
         r.insert(r.end(), r2.begin(), r2.end());
     }
-    
+
     return r;
 }
 
 void Root::refresh(bool aForce)
 {
     bool didStartAny = false;
+
+    // copy all candidate ctalogs to a seperate list, since refreshing
+    // can modify both the main collection and/or the disabled list
+    CatalogList toRefresh;
     CatalogDict::iterator it = d->catalogs.begin();
     for (; it != d->catalogs.end(); ++it) {
-        if (aForce || it->second->needsRefresh()) {
-            it->second->refresh();
-            didStartAny = true;
-        }
+        toRefresh.push_back(it->second);
+    }
+
+    toRefresh.insert(toRefresh.end(), d->disabledCatalogs.begin(),
+                     d->disabledCatalogs.end());
+
+
+    CatalogList::iterator j = toRefresh.begin();
+    for (; j != toRefresh.end(); ++j) {
+        (*j)->refresh();
+        didStartAny =  true;
     }
-    
+
     if (!didStartAny) {
         // signal refresh complete to the delegate already
         d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED);
@@ -368,7 +407,7 @@ void Root::addDelegate(simgear::pkg::Delegate *aDelegate)
 {
     d->delegates.push_back(aDelegate);
 }
-    
+
 void Root::removeDelegate(simgear::pkg::Delegate *aDelegate)
 {
     DelegateVec::iterator it = std::find(d->delegates.begin(),
@@ -378,7 +417,7 @@ void Root::removeDelegate(simgear::pkg::Delegate *aDelegate)
     }
     d->delegates.erase(it);
 }
-    
+
 void Root::setLocale(const std::string& aLocale)
 {
     d->locale = aLocale;
@@ -394,7 +433,7 @@ void Root::scheduleToUpdate(InstallRef aInstall)
     if (!aInstall) {
         throw sg_exception("missing argument to scheduleToUpdate");
     }
-    
+
     PackageList deps = aInstall->package()->dependencies();
     BOOST_FOREACH(Package* dep, deps) {
         // will internally schedule for update if required
@@ -404,7 +443,7 @@ void Root::scheduleToUpdate(InstallRef aInstall)
 
     bool wasEmpty = d->updateDeque.empty();
     d->updateDeque.push_back(aInstall);
-    
+
     if (wasEmpty) {
         aInstall->startUpdate();
     }
@@ -416,7 +455,7 @@ bool Root::isInstallQueued(InstallRef aInstall) const
         std::find(d->updateDeque.begin(), d->updateDeque.end(), aInstall);
     return (it != d->updateDeque.end());
 }
-    
+
 void Root::startInstall(InstallRef aInstall)
 {
     d->fireStartInstall(aInstall);
@@ -434,7 +473,7 @@ void Root::startNext(InstallRef aCurrent)
     } else {
         d->updateDeque.pop_front();
     }
-    
+
     if (!d->updateDeque.empty()) {
         d->updateDeque.front()->startUpdate();
     }
@@ -475,54 +514,88 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
 
     if (aReason == Delegate::STATUS_IN_PROGRESS) {
         d->refreshing.insert(aCat);
-
-        if (catIt == d->catalogs.end()) {
-            // first fresh, add to our storage now
-            d->catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat));
-        }
     } else {
         d->refreshing.erase(aCat);
     }
-    
-    if ((aReason != Delegate::STATUS_REFRESHED) && (aReason != Delegate::STATUS_IN_PROGRESS)) {
-        // if the failure is permanent, delete the catalog from our
-        // list (don't touch it on disk)
-        bool isPermanentFailure = (aReason == Delegate::FAIL_VERSION);
-        if (isPermanentFailure) {
-            SG_LOG(SG_GENERAL, SG_WARN, "permanent failure for catalog:" << aCat->id());
-            if (catIt != d->catalogs.end()) {
-                d->catalogs.erase(catIt);
-            }
+
+    if ((aReason == Delegate::STATUS_REFRESHED) && (catIt == d->catalogs.end())) {
+        assert(!aCat->id().empty());
+        d->catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat));
+
+        // catalog might have been previously disabled, let's remove in that case
+        CatalogList::iterator j = std::find(d->disabledCatalogs.begin(),
+                                            d->disabledCatalogs.end(),
+                                            aCat);
+        if (j != d->disabledCatalogs.end()) {
+            SG_LOG(SG_GENERAL, SG_INFO, "re-enabling disabled catalog:" << aCat->id());
+            d->disabledCatalogs.erase(j);
         }
     }
-    
+
+    if ((aReason != Delegate::STATUS_REFRESHED) &&
+        (aReason != Delegate::STATUS_IN_PROGRESS) &&
+        (aReason != Delegate::STATUS_SUCCESS))
+    {
+        // catalog has errors, disable it
+        CatalogList::iterator j = std::find(d->disabledCatalogs.begin(),
+                                            d->disabledCatalogs.end(),
+                                            aCat);
+        if (j == d->disabledCatalogs.end()) {
+            SG_LOG(SG_GENERAL, SG_INFO, "disabling catalog:" << aCat->id());
+            d->disabledCatalogs.push_back(aCat);
+        }
+
+        // and remove it from the active collection
+        if (catIt != d->catalogs.end()) {
+            d->catalogs.erase(catIt);
+        }
+    } // of catalog has errors case
+
     if (d->refreshing.empty()) {
         d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED);
+        d->firePackagesChanged();
     }
 }
 
 bool Root::removeCatalogById(const std::string& aId)
 {
+    CatalogRef cat;
+
     CatalogDict::iterator catIt = d->catalogs.find(aId);
     if (catIt == d->catalogs.end()) {
-        SG_LOG(SG_GENERAL, SG_WARN, "removeCatalogById: unknown ID:" << aId);
-        return false;
-    }
-    
-    CatalogRef cat = catIt->second;
-    
-    // drop the reference
-    d->catalogs.erase(catIt);
-    
+        // check the disabled list
+        CatalogList::iterator j = d->disabledCatalogs.begin();
+        for (; j != d->disabledCatalogs.end(); ++j) {
+            if ((*j)->id() == aId) {
+                break;
+            }
+        }
+
+        if (j == d->disabledCatalogs.end()) {
+            SG_LOG(SG_GENERAL, SG_WARN, "removeCatalogById: no catalog with id:" << aId);
+            return false;
+        }
+
+        cat = *j;
+        d->disabledCatalogs.erase(j);
+    } else {
+        cat = catIt->second;
+        // drop the reference
+        d->catalogs.erase(catIt);
+    }
+
     bool ok = cat->uninstall();
     if (!ok) {
         SG_LOG(SG_GENERAL, SG_WARN, "removeCatalogById: catalog :" << aId
             << "failed to uninstall");
     }
-    
+
+    // notify that a catalog is being removed
+    d->firePackagesChanged();
+
     return ok;
 }
-    
+
 void Root::requestThumbnailData(const std::string& aUrl)
 {
     MemThumbnailCache::iterator it = d->thumbnailCache.find(aUrl);
@@ -538,7 +611,7 @@ void Root::requestThumbnailData(const std::string& aUrl)
         // in cache but empty data, still fetching
     }
 }
-    
+
 InstallRef Root::existingInstallForPackage(PackageRef p) const
 {
     RootPrivate::InstallCache::const_iterator it =
@@ -556,16 +629,16 @@ InstallRef Root::existingInstallForPackage(PackageRef p) const
         d->m_installs[p] = InstallRef();
         return InstallRef();
     }
-    
+
     return it->second;
 }
-    
+
 void Root::registerInstall(InstallRef ins)
 {
     if (!ins.valid()) {
         return;
     }
-    
+
     d->m_installs[ins->package()] = ins;
 }
 
@@ -574,10 +647,10 @@ void Root::unregisterInstall(InstallRef ins)
     if (!ins .valid()) {
         return;
     }
-    
+
     d->m_installs.erase(ins->package());
 }
-    
+
 } // of namespace pkg
 
 } // of namespace simgear