add more spacing
[personal-kdebase.git] / runtime / nepomuk / services / filewatch / nepomukfilewatch.cpp
blob78d869bcec16f6f04fde1a88633c300c5f91263c
1 /* This file is part of the KDE Project
2 Copyright (c) 2007-2008 Sebastian Trueg <trueg@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
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 Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
19 #include "nepomukfilewatch.h"
21 #include <QtCore/QTimer>
22 #include <QtCore/QDir>
23 #include <QtCore/QRegExp>
24 #include <QtCore/QFileInfo>
25 #include <QtDBus/QDBusConnection>
27 #include <kdebug.h>
28 #include <KUrl>
29 #include <kmessagebox.h>
30 #include <klocale.h>
31 #include <KPluginFactory>
32 #include <kio/netaccess.h>
34 #include <Soprano/Model>
35 #include <Soprano/StatementIterator>
36 #include <Soprano/Statement>
37 #include <Soprano/Node>
38 #include <Soprano/NodeIterator>
39 #include <Soprano/QueryResultIterator>
40 #include <Soprano/Vocabulary/Xesam>
42 // Restrictions and TODO:
43 // ----------------------
45 // * KIO slaves that do change the local file system may emit stuff like
46 // file:///foo/bar -> xyz://foobar while the file actually ends up in
47 // the local file system again. This is not handled here. It is maybe
48 // necessary to use KFileItem::mostLocalUrl to determine local paths
49 // before deciding to call updateMetaDataForResource.
51 // * Only operations done through KIO are caught
54 using namespace Soprano;
57 NEPOMUK_EXPORT_SERVICE( Nepomuk::FileWatch, "nepomukfilewatch")
60 namespace {
61 Soprano::QueryResultIterator queryChildren( Model* model, const QString& path )
63 // escape special chars
64 QString regexpPath( path );
65 if ( regexpPath[regexpPath.length()-1] != '/' ) {
66 regexpPath += '/';
68 regexpPath.replace( QRegExp( "([\\.\\?\\*\\\\+\\(\\)\\\\\\|\\[\\]{}])" ), "\\\\\\1" );
70 // kDebug() << "query:" << QString( "select ?r ?p where { ?r <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#fileUrl> ?p FILTER(REGEX(STR(?p), '^%1')) . }" ).arg( regexpPath );
72 // query for all files that
73 return model->executeQuery( QString( "prefix xesam: <http://freedesktop.org/standards/xesam/1.0/core#> "
74 "select ?r ?p where { ?r xesam:url ?p . FILTER(REGEX(STR(?p), '^%1')) . }" ).arg( regexpPath ),
75 Soprano::Query::QueryLanguageSparql );
81 Nepomuk::FileWatch::FileWatch( QObject* parent, const QList<QVariant>& )
82 : Service( parent ),
83 m_strigiParentUrlUri( "http://strigi.sf.net/ontologies/0.9#parentUrl" )
85 // monitor KIO for changes
86 QDBusConnection::sessionBus().connect( QString(), QString(), "org.kde.KDirNotify", "FileMoved",
87 this, SIGNAL( fileMoved( const QString&, const QString& ) ) );
88 QDBusConnection::sessionBus().connect( QString(), QString(), "org.kde.KDirNotify", "FilesRemoved",
89 this, SIGNAL( filesDeleted( const QStringList& ) ) );
91 // async connection to the actual slots
92 // FIXME: is the signal delivery order guranteed? If not, we need a queue!
93 connect( this, SIGNAL( fileMoved( QString, QString ) ), this, SLOT( slotFileMoved( QString, QString ) ), Qt::QueuedConnection );
94 connect( this, SIGNAL( filesDeleted( QStringList ) ), this, SLOT( slotFilesDeleted( QStringList ) ), Qt::QueuedConnection );
98 Nepomuk::FileWatch::~FileWatch()
103 void Nepomuk::FileWatch::slotFileMoved( const QString& urlFrom, const QString& urlTo )
105 KUrl from( urlFrom );
106 KUrl to( urlTo );
108 kDebug() << from << to;
110 if ( from.isEmpty() || to.isEmpty() ) {
111 kDebug() << "empty path. Looks like a bug somewhere...";
112 return;
115 if ( mainModel() ) {
116 // We do NOT get deleted messages for overwritten files! Thus, we have to remove all metadata for overwritten files
117 // first. We do that now.
118 removeMetaData( to );
120 // and finally update the old statements
121 updateMetaData( from, to );
123 // update children files in case from is a folder
124 QString fromPath = from.path();
125 QList<Soprano::BindingSet> children = queryChildren( mainModel(), fromPath ).allBindings();
126 foreach( const Soprano::BindingSet& bs, children ) {
127 QString path = to.path();
128 if ( !path.endsWith( '/' ) )
129 path += '/';
130 path += bs[1].toString().mid( fromPath.endsWith( '/' ) ? fromPath.length() : fromPath.length()+1 );
131 updateMetaData( bs[1].toString(), path ); // FIXME: reuse the URI we already have
134 // TODO: optionally create a xesam:url property in case a file was moved from a remote URL to a local one
135 // still disabled since we also need a new context and that is much easier with a proper NRLModel which
136 // we will hopefully have in Soprano 2.2
137 // if ( to.isLocalFile() ) {
138 // if ( !mainModel()->containsAnyStatement( to, Soprano::Vocabulary::Xesam::url(), Soprano::Node() ) ) {
139 // mainModel()->addStatement( to, Soprano::Vocabulary::Xesam::url(), Soprano::LiteralValue( to.path() ) );
140 // }
141 // }
143 else {
144 kDebug() << "Could not contact Nepomuk server.";
149 void Nepomuk::FileWatch::slotFilesDeleted( const QStringList& paths )
151 foreach( const QString& path, paths ) {
152 slotFileDeleted( path );
157 void Nepomuk::FileWatch::slotFileDeleted( const QString& urlString )
159 KUrl url( urlString );
161 kDebug() << url;
163 if ( mainModel() ) {
164 removeMetaData( url );
166 // remove child annotations in case it is a local folder
167 foreach( Soprano::Node node, queryChildren( mainModel(), url.path() ).iterateBindings( 0 ).allNodes() ) {
168 mainModel()->removeAllStatements( Statement( node, Node(), Node() ) );
171 else {
172 kDebug() << "Could not contact Nepomuk server.";
177 void Nepomuk::FileWatch::removeMetaData( const KUrl& url )
179 kDebug() << url;
181 if ( url.isEmpty() ) {
182 kDebug() << "empty path. Looks like a bug somewhere...";
183 return;
186 mainModel()->removeAllStatements( Statement( url, Node(), Node() ) );
188 // FIXME: what about the triples that have our uri as object?
192 void Nepomuk::FileWatch::updateMetaData( const KUrl& from, const KUrl& to )
194 kDebug() << from << "->" << to;
197 // Nepomuk allows annotating of remote files. These files do not necessarily have a xesam:url property
198 // since it would not be of much use in the classic sense: we cannot use it to locate the file on the hd
200 // Thus, when remote files are moved through KIO and we get the notification, we simply change all triples
201 // referring to the original file to use the new URL
204 Soprano::Node oldResource = from;
205 Soprano::Node newResource = to;
207 // update the resource itself
208 // -----------------------------------------------
209 if ( mainModel()->containsAnyStatement( Soprano::Statement( oldResource, Soprano::Node(), Soprano::Node() ) ) ) {
211 QList<Soprano::Statement> sl = mainModel()->listStatements( Soprano::Statement( oldResource,
212 Soprano::Node(),
213 Soprano::Node() ) ).allStatements();
214 Q_FOREACH( Soprano::Statement s, sl ) {
215 if ( s.predicate() == Soprano::Vocabulary::Xesam::url() ) {
216 mainModel()->addStatement( Soprano::Statement( newResource,
217 s.predicate(),
218 Soprano::LiteralValue( to.path() ),
219 s.context() ) );
221 else if ( s.predicate() == m_strigiParentUrlUri ) {
222 mainModel()->addStatement( Soprano::Statement( newResource,
223 s.predicate(),
224 Soprano::LiteralValue( to.directory( KUrl::IgnoreTrailingSlash ) ),
225 s.context() ) );
227 else {
228 mainModel()->addStatement( Soprano::Statement( newResource,
229 s.predicate(),
230 s.object(),
231 s.context() ) );
235 mainModel()->removeStatements( sl );
236 // -----------------------------------------------
239 // update resources relating to it
240 // -----------------------------------------------
241 sl = mainModel()->listStatements( Statement( Node(),
242 Node(),
243 oldResource ) ).allStatements();
244 Q_FOREACH( Soprano::Statement s, sl ) {
245 mainModel()->addStatement( Soprano::Statement( s.subject(),
246 s.predicate(),
247 newResource,
248 s.context() ) );
250 mainModel()->removeStatements( sl );
251 // -----------------------------------------------
255 #include "nepomukfilewatch.moc"