• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

NepomukDaemons

searchfolder.cpp

Go to the documentation of this file.
00001 /*
00002    Copyright (C) 2008 by Sebastian Trueg <trueg at kde.org>
00003 
00004    This program is free software; you can redistribute it and/or modify
00005    it under the terms of the GNU General Public License as published by
00006    the Free Software Foundation; either version 2, or (at your option)
00007    any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012    GNU General Public License for more details.
00013 
00014    You should have received a copy of the GNU General Public License
00015    along with this program; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00017  */
00018 
00019 #include "searchfolder.h"
00020 
00021 #include "queryserviceclient.h"
00022 
00023 #include <Soprano/Vocabulary/Xesam>
00024 #include <Soprano/Vocabulary/NAO>
00025 
00026 #include <Nepomuk/Variant>
00027 
00028 #include <QtCore/QMutexLocker>
00029 
00030 #include <KUrl>
00031 #include <KDebug>
00032 #include <KIO/Job>
00033 #include <KIO/NetAccess>
00034 #include <KUser>
00035 #include <kdirnotify.h>
00036 #include <qurlhash.h>
00037 
00038 namespace {
00039     QString addCounterToFileName( const QString& name, int i )
00040     {
00041         QString newName( name );
00042 
00043         int start = name.lastIndexOf('.');
00044         if (start != -1) {
00045             // has a . somewhere, e.g. it has an extension
00046             newName.insert(start, QString::number( i ));
00047         }
00048         else {
00049             // no extension, just tack it on to the end
00050             newName += QString::number( i );
00051         }
00052         return newName;
00053     }
00054 }
00055 
00056 
00057 Nepomuk::SearchEntry::SearchEntry( const QUrl& res,
00058                                    const KIO::UDSEntry& uds )
00059     : m_resource( res ),
00060       m_entry( uds )
00061 {
00062 }
00063 
00064 
00065 Nepomuk::SearchFolder::SearchFolder()
00066 {
00067     // try to force clients to invalidate their cache
00068     org::kde::KDirNotify::emitFilesAdded( "nepomuksearch:/" + m_name );
00069 }
00070 
00071 
00072 Nepomuk::SearchFolder::SearchFolder( const QString& name, const Search::Query& query, KIO::SlaveBase* slave )
00073     : QThread(),
00074       m_name( name ),
00075       m_query( query ),
00076       m_initialListingFinished( false ),
00077       m_slave( slave ),
00078       m_listEntries( false ),
00079       m_statingStarted( false )
00080 {
00081     kDebug() << name << QThread::currentThread();
00082     Q_ASSERT( !name.isEmpty() );
00083 
00084     qRegisterMetaType<QList<QUrl> >();
00085 }
00086 
00087 
00088 Nepomuk::SearchFolder::~SearchFolder()
00089 {
00090     kDebug() << m_name << QThread::currentThread();
00091 
00092     // properly shut down the search thread
00093     quit();
00094     wait();
00095 
00096     qDeleteAll( m_entries );
00097 }
00098 
00099 
00100 void Nepomuk::SearchFolder::run()
00101 {
00102     kDebug() << m_name << QThread::currentThread();
00103 
00104     m_client = new Nepomuk::Search::QueryServiceClient();
00105 
00106     // results signals are connected directly to update the results cache m_results
00107     // and the entries cache m_entries, as well as emitting KDirNotify signals
00108     // a queued connection is not possible since we have no event loop after the
00109     // initial listing which means that queued signals would never get delivered
00110     connect( m_client, SIGNAL( newEntries( const QList<Nepomuk::Search::Result>& ) ),
00111              this, SLOT( slotNewEntries( const QList<Nepomuk::Search::Result>& ) ),
00112              Qt::DirectConnection );
00113     connect( m_client, SIGNAL( entriesRemoved( const QList<QUrl>& ) ),
00114              this, SLOT( slotEntriesRemoved( const QList<QUrl>& ) ),
00115              Qt::DirectConnection );
00116 
00117     // slotFinishedListing needs to be called in the GUi thread
00118     connect( m_client, SIGNAL( finishedListing() ),
00119              this, SLOT( slotFinishedListing() ),
00120              Qt::QueuedConnection );
00121 
00122     m_client->query( m_query );
00123     exec();
00124     delete m_client;
00125 
00126     kDebug() << m_name << "done";
00127 }
00128 
00129 
00130 void Nepomuk::SearchFolder::list()
00131 {
00132     kDebug() << m_name << QThread::currentThread();
00133 
00134     m_listEntries = !m_initialListingFinished;
00135     m_statEntry = false;
00136 
00137     if ( !isRunning() ) {
00138         start();
00139     }
00140     else {
00141         // list all cached entries
00142         for ( QHash<QString, SearchEntry*>::const_iterator it = m_entries.constBegin();
00143               it != m_entries.constEnd(); ++it ) {
00144             m_slave->listEntry( ( *it )->entry(), false );
00145         }
00146 
00147         // if there is nothing more to list...
00148         if ( m_initialListingFinished &&
00149             m_results.isEmpty() ) {
00150             m_slave->listEntry( KIO::UDSEntry(), true );
00151             m_slave->finished();
00152         }
00153         else {
00154             m_listEntries = true;
00155         }
00156     }
00157 
00158     // if we have more to list
00159     if ( m_listEntries ) {
00160         if ( !m_statingStarted ) {
00161             QTimer::singleShot( 0, this, SLOT( slotStatNextResult() ) );
00162         }
00163         kDebug() << "entering loop" << m_name << QThread::currentThread();
00164         m_loop.exec();
00165     }
00166 }
00167 
00168 
00169 void Nepomuk::SearchFolder::stat( const QString& name )
00170 {
00171     kDebug() << name;
00172 
00173     if ( SearchEntry* entry = findEntry( name ) ) {
00174         m_slave->statEntry( entry->entry() );
00175         m_slave->finished();
00176     }
00177     else if ( !isRunning() ||
00178               !m_results.isEmpty() ) {
00179         m_nameToStat = name;
00180         m_statEntry = true;
00181         m_listEntries = false;
00182 
00183         if ( !isRunning() ) {
00184             start();
00185         }
00186 
00187         if ( !m_statingStarted ) {
00188             QTimer::singleShot( 0, this, SLOT( slotStatNextResult() ) );
00189         }
00190         m_loop.exec();
00191     }
00192     else {
00193         m_slave->error( KIO::ERR_DOES_NOT_EXIST, "nepomuksearch:/" + m_name + '/' + name );
00194     }
00195 }
00196 
00197 
00198 Nepomuk::SearchEntry* Nepomuk::SearchFolder::findEntry( const QString& name ) const
00199 {
00200     kDebug() << name;
00201 
00202     QHash<QString, SearchEntry*>::const_iterator it = m_entries.find( name );
00203     if ( it != m_entries.end() ) {
00204         kDebug() << "-----> found";
00205         return *it;
00206     }
00207     else {
00208         kDebug() << "-----> not found";
00209         return 0;
00210     }
00211 }
00212 
00213 
00214 Nepomuk::SearchEntry* Nepomuk::SearchFolder::findEntry( const KUrl& url ) const
00215 {
00216     // FIXME
00217     return 0;
00218 }
00219 
00220 
00221 // always called in search thread
00222 void Nepomuk::SearchFolder::slotNewEntries( const QList<Nepomuk::Search::Result>& results )
00223 {
00224     kDebug() << m_name << QThread::currentThread();
00225 
00226     m_resultMutex.lock();
00227     m_results += results;
00228     m_resultMutex.unlock();
00229 
00230     if ( m_initialListingFinished ) {
00231         // inform everyone of the change
00232         kDebug() << ( "Informing about change in folder nepomuksearch:/" + m_name );
00233         org::kde::KDirNotify::emitFilesAdded( "nepomuksearch:/" + m_name );
00234     }
00235 }
00236 
00237 
00238 // always called in search thread
00239 void Nepomuk::SearchFolder::slotEntriesRemoved( const QList<QUrl>& entries )
00240 {
00241     kDebug() << QThread::currentThread();
00242 
00243     QMutexLocker lock( &m_resultMutex );
00244 
00245     foreach( const QUrl& uri, entries ) {
00246         QHash<QUrl, QString>::iterator it = m_resourceNameMap.find( uri );
00247         if ( it != m_resourceNameMap.end() ) {
00248             delete m_entries.take( it.value() );
00249 
00250             // inform everybody
00251             org::kde::KDirNotify::emitFilesRemoved( QStringList() << ( "nepomuksearch:/" + m_name + '/' + *it ) );
00252 
00253             m_resourceNameMap.erase( it );
00254         }
00255     }
00256 }
00257 
00258 
00259 // always called in search thread
00260 void Nepomuk::SearchFolder::slotFinishedListing()
00261 {
00262     kDebug() << m_name << QThread::currentThread();
00263     m_initialListingFinished = true;
00264     wrap();
00265 }
00266 
00267 
00268 // always called in main thread
00269 void Nepomuk::SearchFolder::slotStatNextResult()
00270 {
00271 //    kDebug();
00272     m_statingStarted = true;
00273 
00274     while ( 1 ) {
00275         // never lock the mutex for the whole duration of the method
00276         // since it may start an event loop which can result in more
00277         // newEntries signals to be delivered which would result in
00278         // a deadlock
00279         m_resultMutex.lock();
00280         if( !m_results.isEmpty() ) {
00281             Search::Result result = m_results.dequeue();
00282             m_resultMutex.unlock();
00283             SearchEntry* entry = statResult( result );
00284             if ( entry ) {
00285                 if ( m_listEntries ) {
00286                     kDebug() << "listing" << entry->resource();
00287                     m_slave->listEntry( entry->entry(), false );
00288                 }
00289                 else if ( m_statEntry ) {
00290                     if ( m_nameToStat == entry->entry().stringValue( KIO::UDSEntry::UDS_NAME ) ) {
00291                         kDebug() << "stating" << entry->resource();
00292                         m_nameToStat = QString();
00293                         m_slave->statEntry( entry->entry() );
00294                     }
00295                 }
00296             }
00297         }
00298         else {
00299             m_resultMutex.unlock();
00300             break;
00301         }
00302     }
00303 
00304     if ( !m_results.isEmpty() ||
00305          !m_initialListingFinished ) {
00306         // we need to use the timer since statResource does only create an event loop
00307         // for files, not for arbitrary resources.
00308         QTimer::singleShot( 0, this, SLOT( slotStatNextResult() ) );
00309     }
00310     else {
00311         m_statingStarted = false;
00312         wrap();
00313     }
00314 }
00315 
00316 
00317 // always called in main thread
00318 void Nepomuk::SearchFolder::wrap()
00319 {
00320     kDebug() << m_name << QThread::currentThread();
00321 
00322     if ( m_results.isEmpty() &&
00323          m_initialListingFinished &&
00324          m_loop.isRunning() ) {
00325         if ( m_listEntries ) {
00326             kDebug() << "listing done";
00327             m_slave->listEntry( KIO::UDSEntry(), true );
00328             m_slave->finished();
00329         }
00330         else if ( m_statEntry ) {
00331             if ( !m_nameToStat.isEmpty() ) {
00332                 // if m_nameToStat is not empty the name was not found during listing which means that
00333                 // it does not exist
00334                 m_slave->error( KIO::ERR_DOES_NOT_EXIST, "nepomuksearch:/" + m_name + '/' + m_nameToStat );
00335                 m_nameToStat = QString();
00336             }
00337             else
00338                 m_slave->finished();
00339         }
00340 
00341         m_statingStarted = false;
00342         m_listEntries = false;
00343         m_statEntry = false;
00344         kDebug() << m_name << QThread::currentThread() << "exiting loop";
00345         m_loop.exit();
00346     }
00347 }
00348 
00349 
00350 // always called in main thread
00351 Nepomuk::SearchEntry* Nepomuk::SearchFolder::statResult( const Search::Result& result )
00352 {
00353     kDebug() << result.resourceUri();
00354 
00355     KIO::UDSEntry uds;
00356 
00357     KUrl url = result[Soprano::Vocabulary::Xesam::url()].toString();
00358     if ( url.isEmpty() ) {
00359         url = result.resourceUri();
00360     }
00361     bool isFile = false;
00362     if ( !url.isEmpty() && url.scheme() != "akonadi" ) { // do not stat akonadi resouces here, way too slow, even hangs if akonadi is not running
00363         kDebug() << "listing file" << url;
00364         if ( KIO::StatJob* job = KIO::stat( url, KIO::HideProgressInfo ) ) {
00365             job->setAutoDelete( false );
00366             if ( KIO::NetAccess::synchronousRun( job, 0 ) ) {
00367                 uds = job->statResult();
00368                 if ( url.isLocalFile() ) {
00369                     uds.insert( KIO::UDSEntry::UDS_LOCAL_PATH, url.toLocalFile() );
00370                 }
00371                 isFile = true;
00372             }
00373             else {
00374                 kDebug() << "failed to stat" << url;
00375             }
00376             delete job;
00377         }
00378     }
00379 
00380     //
00381     // The nepomuk resource listing is the same as in the nepomuk kio slave.
00382     // So either only depend on that or let the nepomuk kio slave fail on each
00383     // stat. (the latter means that we need the nepomuk kio slave in kdebase)
00384     //
00385     if ( !isFile ) {
00386         kDebug() << "listing resource" << result.resourceUri();
00387 
00388         Nepomuk::Resource res( result.resourceUri() );
00389 
00390         QString name = res.genericLabel();
00391 
00392         // make sure name is not the URI (which is the fallback of genericLabel() and will lead to crashes in KDirModel)
00393         if ( name.contains( '/' ) ) {
00394             name = name.section( '/', -1 );
00395             if ( name.isEmpty() )
00396                 name = res.resourceUri().fragment();
00397             if ( name.isEmpty() )
00398                 name = res.resourceUri().toString().replace( '/', '_' );
00399         }
00400 
00401         uds.insert( KIO::UDSEntry::UDS_NAME, name );
00402         uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, name );
00403 
00404         QString icon = res.genericIcon();
00405         if ( !icon.isEmpty() ) {
00406             uds.insert( KIO::UDSEntry::UDS_ICON_NAME, icon );
00407         }
00408 
00409         uds.insert( KIO::UDSEntry::UDS_CREATION_TIME, res.property( Soprano::Vocabulary::NAO::created() ).toDateTime().toTime_t() );
00410 
00411         uds.insert( KIO::UDSEntry::UDS_ACCESS, 0700 );
00412         uds.insert( KIO::UDSEntry::UDS_USER, KUser().loginName() );
00413 
00414 //    uds.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
00415         uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, "application/x-nepomuk-resource" );
00416     }
00417 
00418     uds.insert( KIO::UDSEntry::UDS_TARGET_URL, result.resourceUri().toString() );
00419 
00420     //
00421     // make sure we have no duplicate names
00422     //
00423     QString name = uds.stringValue( KIO::UDSEntry::UDS_DISPLAY_NAME );
00424     if ( name.isEmpty() ) {
00425         name = uds.stringValue( KIO::UDSEntry::UDS_NAME );
00426     }
00427 
00428     // the name is empty if the resource could not be stated
00429     if ( !name.isEmpty() ) {
00430         int cnt = 0;
00431         if ( m_nameCntHash.contains( name ) ) {
00432             cnt = ++m_nameCntHash[name];
00433         }
00434         else {
00435             cnt = m_nameCntHash[name] = 0;
00436         }
00437         if ( cnt >= 1 ) {
00438             name = addCounterToFileName( name, cnt );
00439         }
00440         uds.insert( KIO::UDSEntry::UDS_NAME, name );
00441         uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, name );
00442 
00443         SearchEntry* entry = new SearchEntry( result.resourceUri(), uds );
00444         m_entries.insert( name, entry );
00445         m_resourceNameMap.insert( result.resourceUri(), name );
00446 
00447         kDebug() << "Stating" << result.resourceUri() << "done";
00448 
00449         return entry;
00450     }
00451     else {
00452         // no valid name -> no valid uds
00453         kDebug() << "Stating" << result.resourceUri() << "failed";
00454         return 0;
00455     }
00456 }

NepomukDaemons

Skip menu "NepomukDaemons"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KCMShell
  • KNotify
  • KStyles
  • Nepomuk Daemons
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal