Sophie

Sophie

distrib > Mandriva > 2011.0 > x86_64 > by-pkgid > c540015029127156acd153c7f8af8866 > files > 1

kdebase4-runtime-4.6.5-9.src.rpm

diff --git a/nepomuk/CMakeLists.txt b/nepomuk/CMakeLists.txt
index 0510b54..c6a1879 100644
--- a/nepomuk/CMakeLists.txt
+++ b/nepomuk/CMakeLists.txt
@@ -10,14 +10,15 @@ include_directories(
   ${nepomuk_BINARY_DIR}/common
 )
 
+add_subdirectory(ontologies)
 add_subdirectory(common)
 add_subdirectory(server)
 add_subdirectory(kcm)
 add_subdirectory(services)
 add_subdirectory(servicestub)
 add_subdirectory(kioslaves)
+add_subdirectory(controller)
 add_subdirectory(interfaces)
-add_subdirectory(ontologies)
 add_subdirectory(removed-services)
 
 #add_subdirectory(test)
diff --git a/nepomuk/common/CMakeLists.txt b/nepomuk/common/CMakeLists.txt
index 39b400f..57892fc 100644
--- a/nepomuk/common/CMakeLists.txt
+++ b/nepomuk/common/CMakeLists.txt
@@ -3,6 +3,7 @@ project(nepomuk_common)
 set(nepomukcommon_SRCS
   fileexcludefilters.cpp
   regexpcache.cpp
+  removablemediacache.cpp
 )
 
 kde4_add_library(nepomukcommon SHARED ${nepomukcommon_SRCS})
@@ -10,6 +11,7 @@ kde4_add_library(nepomukcommon SHARED ${nepomukcommon_SRCS})
 target_link_libraries(nepomukcommon
   ${QT_QTCORE_LIBRARY}
   ${KDE4_KDECORE_LIBRARY}
+  ${KDE4_SOLID_LIBRARY}
   ${NEPOMUK_LIBRARIES}
   )
 
diff --git a/nepomuk/common/nepomuktools.h b/nepomuk/common/nepomuktools.h
new file mode 100644
index 0000000..64817d5
--- /dev/null
+++ b/nepomuk/common/nepomuktools.h
@@ -0,0 +1,61 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef NEPOMUKTOOLS_H
+#define NEPOMUKTOOLS_H
+
+#include <KUrl>
+#include <QtCore/QStringList>
+#include <Soprano/Node>
+
+namespace Nepomuk {
+const int MAX_SPLIT_LIST_ITEMS = 20;
+
+/**
+ * Convert a list or set or QUrls into a list of N3 formatted strings.
+ */
+template<typename T> QStringList resourcesToN3(const T& urls) {
+    QStringList n3;
+    Q_FOREACH(const QUrl& url, urls) {
+        n3 << Soprano::Node::resourceToN3(url);
+    }
+    return n3;
+}
+
+/**
+ * Split a list into several lists, each not containing more than \p max items
+ */
+template<typename T> QList<QList<T> > splitList(const QList<T>& list, int max = MAX_SPLIT_LIST_ITEMS) {
+    QList<QList<T> > splitted;
+    int i = 0;
+    QList<T> single;
+    foreach(const T& item, list) {
+        single.append(item);
+        if(++i >= max) {
+            splitted << single;
+            single.clear();
+        }
+    }
+    return splitted;
+}
+}
+
+#endif // NEPOMUKTOOLS_H
diff --git a/nepomuk/common/removablemediacache.cpp b/nepomuk/common/removablemediacache.cpp
new file mode 100644
index 0000000..54afdc2
--- /dev/null
+++ b/nepomuk/common/removablemediacache.cpp
@@ -0,0 +1,286 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "removablemediacache.h"
+
+#include <Solid/DeviceNotifier>
+#include <Solid/DeviceInterface>
+#include <Solid/Block>
+#include <Solid/Device>
+#include <Solid/StorageDrive>
+#include <Solid/StorageVolume>
+#include <Solid/StorageAccess>
+#include <Solid/NetworkShare>
+#include <Solid/OpticalDisc>
+#include <Solid/Predicate>
+
+#include <KDebug>
+
+#include <QtCore/QMutexLocker>
+
+
+namespace {
+    bool isUsableVolume( const Solid::Device& dev ) {
+        if ( dev.is<Solid::StorageAccess>() ) {
+            if( dev.is<Solid::StorageVolume>() &&
+                    dev.parent().is<Solid::StorageDrive>() &&
+                    ( dev.parent().as<Solid::StorageDrive>()->isRemovable() ||
+                      dev.parent().as<Solid::StorageDrive>()->isHotpluggable() ) ) {
+                const Solid::StorageVolume* volume = dev.as<Solid::StorageVolume>();
+                if ( !volume->isIgnored() && volume->usage() == Solid::StorageVolume::FileSystem )
+                    return true;
+            }
+            else if(dev.is<Solid::NetworkShare>()) {
+                return !dev.as<Solid::NetworkShare>()->url().isEmpty();
+            }
+        }
+
+        // fallback
+        return false;
+    }
+
+    bool isUsableVolume( const QString& udi ) {
+        Solid::Device dev( udi );
+        return isUsableVolume( dev );
+    }
+}
+
+
+Nepomuk::RemovableMediaCache::RemovableMediaCache(QObject *parent)
+    : QObject(parent)
+{
+    initCacheEntries();
+
+    connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceAdded( const QString& ) ),
+             this, SLOT( slotSolidDeviceAdded( const QString& ) ) );
+    connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceRemoved( const QString& ) ),
+             this, SLOT( slotSolidDeviceRemoved( const QString& ) ) );
+}
+
+
+Nepomuk::RemovableMediaCache::~RemovableMediaCache()
+{
+}
+
+
+void Nepomuk::RemovableMediaCache::initCacheEntries()
+{
+    QList<Solid::Device> devices
+            = Solid::Device::listFromQuery(QLatin1String("StorageVolume.usage=='FileSystem'"))
+            + Solid::Device::listFromType(Solid::DeviceInterface::NetworkShare);
+    foreach( const Solid::Device& dev, devices ) {
+        if ( isUsableVolume( dev ) ) {
+            if(Entry* entry = createCacheEntry( dev )) {
+                const Solid::StorageAccess* storage = entry->device().as<Solid::StorageAccess>();
+                if ( storage && storage->isAccessible() )
+                    slotAccessibilityChanged( true, dev.udi() );
+            }
+        }
+    }
+}
+
+QList<const Nepomuk::RemovableMediaCache::Entry *> Nepomuk::RemovableMediaCache::allMedia() const
+{
+    QList<const Entry*> media;
+    for(QHash<QString, Entry>::const_iterator it = m_metadataCache.begin(); it != m_metadataCache.end(); ++it)
+        media.append(&(*it));
+    return media;
+}
+
+Nepomuk::RemovableMediaCache::Entry* Nepomuk::RemovableMediaCache::createCacheEntry( const Solid::Device& dev )
+{
+    QMutexLocker lock(&m_entryCacheMutex);
+
+    Entry entry(dev);
+    if(!entry.url().isEmpty()) {
+        kDebug() << "Usable" << dev.udi();
+
+        // we only add to this set and never remove. This is no problem as this is a small set
+        m_usedSchemas.insert(KUrl(entry.url()).scheme());
+
+        connect( dev.as<Solid::StorageAccess>(), SIGNAL(accessibilityChanged(bool, QString)),
+                 this, SLOT(slotAccessibilityChanged(bool, QString)) );
+
+        m_metadataCache.insert( dev.udi(), entry );
+
+        emit deviceAdded(&m_metadataCache[dev.udi()]);
+
+        return &m_metadataCache[dev.udi()];
+    }
+    else {
+        kDebug() << "Cannot use device due to empty identifier:" << dev.udi();
+        return 0;
+    }
+}
+
+
+const Nepomuk::RemovableMediaCache::Entry* Nepomuk::RemovableMediaCache::findEntryByFilePath( const QString& path ) const
+{
+    QMutexLocker lock(&m_entryCacheMutex);
+
+    for( QHash<QString, Entry>::const_iterator it = m_metadataCache.begin();
+         it != m_metadataCache.end(); ++it ) {
+        const Entry& entry = *it;
+        if ( entry.device().as<Solid::StorageAccess>()->isAccessible() &&
+             path.startsWith( entry.device().as<Solid::StorageAccess>()->filePath() ) )
+            return &entry;
+    }
+
+    return 0;
+}
+
+
+const Nepomuk::RemovableMediaCache::Entry* Nepomuk::RemovableMediaCache::findEntryByUrl(const KUrl &url) const
+{
+    QMutexLocker lock(&m_entryCacheMutex);
+
+    for( QHash<QString, Entry>::const_iterator it = m_metadataCache.constBegin();
+        it != m_metadataCache.constEnd(); ++it ) {
+        const Entry& entry = *it;
+        kDebug() << url << entry.url();
+        if(url.url().startsWith(entry.url())) {
+            return &entry;
+        }
+    }
+
+    return 0;
+}
+
+
+bool Nepomuk::RemovableMediaCache::hasRemovableSchema(const KUrl &url) const
+{
+    return m_usedSchemas.contains(url.scheme());
+}
+
+
+void Nepomuk::RemovableMediaCache::slotSolidDeviceAdded( const QString& udi )
+{
+    kDebug() << udi;
+
+    if ( isUsableVolume( udi ) ) {
+        createCacheEntry( Solid::Device( udi ) );
+    }
+}
+
+
+void Nepomuk::RemovableMediaCache::slotSolidDeviceRemoved( const QString& udi )
+{
+    kDebug() << udi;
+    if ( m_metadataCache.contains( udi ) ) {
+        kDebug() << "Found removable storage volume for Nepomuk undocking:" << udi;
+        m_metadataCache.remove( udi );
+    }
+}
+
+
+void Nepomuk::RemovableMediaCache::slotAccessibilityChanged( bool accessible, const QString& udi )
+{
+    kDebug() << accessible << udi;
+
+    //
+    // cache new mount path
+    //
+    if ( accessible ) {
+        QMutexLocker lock(&m_entryCacheMutex);
+        Entry* entry = &m_metadataCache[udi];
+        kDebug() << udi << "accessible at" << entry->device().as<Solid::StorageAccess>()->filePath() << "with identifier" << entry->url();
+        emit deviceMounted(entry);
+    }
+}
+
+
+Nepomuk::RemovableMediaCache::Entry::Entry()
+{
+}
+
+
+Nepomuk::RemovableMediaCache::Entry::Entry(const Solid::Device& device)
+    : m_device(device)
+{
+    if(device.is<Solid::StorageVolume>()) {
+        const Solid::StorageVolume* volume = m_device.as<Solid::StorageVolume>();
+        if(device.is<Solid::OpticalDisc>()) {
+            // we use the label as is - it is not even close to unique but
+            // so far we have nothing better
+            m_urlPrefix = QLatin1String("optical://") + volume->label();
+        }
+        else if(!volume->uuid().isEmpty()) {
+            // we always use lower-case uuids
+            m_urlPrefix = QLatin1String("filex://") + volume->uuid().toLower();
+        }
+    }
+    else if(device.is<Solid::NetworkShare>()) {
+        m_urlPrefix = device.as<Solid::NetworkShare>()->url().toString();
+    }
+}
+
+KUrl Nepomuk::RemovableMediaCache::Entry::constructRelativeUrl( const QString& path ) const
+{
+    if(const Solid::StorageAccess* sa = m_device.as<Solid::StorageAccess>()) {
+        if(sa->isAccessible()) {
+            const QString relativePath = path.mid( sa->filePath().count() );
+            return KUrl( m_urlPrefix + relativePath );
+        }
+    }
+
+    // fallback
+    return KUrl();
+}
+
+
+QString Nepomuk::RemovableMediaCache::Entry::constructLocalPath( const KUrl& filexUrl ) const
+{
+    if(const Solid::StorageAccess* sa = m_device.as<Solid::StorageAccess>()) {
+        if(sa->isAccessible()) {
+            // the base of the path: the mount path
+            QString path( sa->filePath() );
+            if ( path.endsWith( QLatin1String( "/" ) ) )
+                path.truncate( path.length()-1 );
+
+            return path + filexUrl.url().mid(m_urlPrefix.count());
+        }
+    }
+
+    // fallback
+    return QString();
+}
+
+QString Nepomuk::RemovableMediaCache::Entry::mountPath() const
+{
+    if(const Solid::StorageAccess* sa = m_device.as<Solid::StorageAccess>()) {
+        return sa->filePath();
+    }
+    else {
+        return QString();
+    }
+}
+
+bool Nepomuk::RemovableMediaCache::Entry::isMounted() const
+{
+    if(const Solid::StorageAccess* sa = m_device.as<Solid::StorageAccess>()) {
+        return sa->isAccessible();
+    }
+    else {
+        return false;
+    }
+}
+
+#include "removablemediacache.moc"
diff --git a/nepomuk/common/removablemediacache.h b/nepomuk/common/removablemediacache.h
new file mode 100644
index 0000000..25982f0
--- /dev/null
+++ b/nepomuk/common/removablemediacache.h
@@ -0,0 +1,114 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef NEPOMUK_REMOVABLEMEDIACACHE_H
+#define NEPOMUK_REMOVABLEMEDIACACHE_H
+
+#include "nepomukcommon_export.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QMutex>
+#include <QtCore/QSet>
+
+#include <Solid/Device>
+
+#include <KUrl>
+
+
+namespace Nepomuk {
+
+/**
+ * The removable media cache provides access to all removable
+ * media that are supported by Nepomuk. It allows to convert
+ * URLs the way RemovableMediaModel requires it and provides
+ * more or less unique URIs for each device allowing to store
+ * device-specific configuration.
+ */
+class NEPOMUKCOMMON_EXPORT RemovableMediaCache : public QObject
+{
+    Q_OBJECT
+
+public:
+    RemovableMediaCache(QObject *parent = 0);
+    ~RemovableMediaCache();
+
+    class Entry {
+    public:
+        Entry();
+        Entry(const Solid::Device& device);
+
+        KUrl constructRelativeUrl( const QString& path ) const;
+        QString constructLocalPath( const KUrl& filexUrl ) const;
+
+        Solid::Device device() const { return m_device; }
+        QString url() const { return m_urlPrefix; }
+
+        bool isMounted() const;
+        QString mountPath() const;
+
+    private:
+        Solid::Device m_device;
+
+        /// The prefix to be used for URLs
+        QString m_urlPrefix;
+    };
+
+    const Entry* findEntryByFilePath( const QString& path ) const;
+    const Entry* findEntryByUrl(const KUrl& url) const;
+
+    QList<const Entry*> allMedia() const;
+
+    /**
+     * Returns true if the URL might be pointing to a file on a
+     * removable device as handled by this class, ie. a non-local
+     * URL which can be converted to a local one.
+     * This method is primarily used for performance gain.
+     */
+    bool hasRemovableSchema(const KUrl& url) const;
+
+signals:
+    void deviceAdded(const Nepomuk::RemovableMediaCache::Entry* entry);
+    void deviceMounted(const Nepomuk::RemovableMediaCache::Entry* entry);
+
+private slots:
+    void slotSolidDeviceAdded(const QString &udi);
+    void slotSolidDeviceRemoved(const QString &udi);
+    void slotAccessibilityChanged(bool accessible, const QString &udi);
+
+private:
+    void initCacheEntries();
+
+    Entry* createCacheEntry( const Solid::Device& dev );
+
+    /// maps Solid UDI to Entry
+    QHash<QString, Entry> m_metadataCache;
+
+    /// contains all schemas that are used as url prefixes in m_metadataCache
+    /// this is used to avoid trying to convert each and every resource in
+    /// convertFilexUrl
+    QSet<QString> m_usedSchemas;
+
+    mutable QMutex m_entryCacheMutex;
+};
+
+} // namespace Nepomuk
+
+#endif // NEPOMUK_REMOVABLEMEDIACACHE_H
diff --git a/nepomuk/controller/CMakeLists.txt b/nepomuk/controller/CMakeLists.txt
new file mode 100644
index 0000000..76c452d
--- /dev/null
+++ b/nepomuk/controller/CMakeLists.txt
@@ -0,0 +1,41 @@
+project(nepomukcontroller)
+
+include_directories(
+  ${QT_INCLUDES}
+  ${KDE4_INCLUDES}
+  ${SOPRANO_INCLUDE_DIR}
+  ${NEPOMUK_INCLUDE_DIR}
+  ${CMAKE_CURRENT_BUILD_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/../kcm
+  )
+
+set(controller_SRCS
+  main.cpp
+  systray.cpp
+  ../kcm/statuswidget.cpp
+  )
+
+qt4_add_dbus_interface(controller_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.kde.nepomuk.Strigi.xml strigiserviceinterface)
+qt4_add_dbus_interface(controller_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.kde.nepomuk.ServiceControl.xml servicecontrol)
+
+kde4_add_ui_files(controller_SRCS ../kcm/statuswidget.ui)
+
+kde4_add_executable(nepomukcontroller ${controller_SRCS})
+
+target_link_libraries(nepomukcontroller
+  ${KDE4_KDEUI_LIBS}
+  ${KDE4_KIO_LIBS}
+  ${KDE4_SOLID_LIBS}
+  ${NEPOMUK_LIBRARIES}
+  ${SOPRANO_LIBRARIES}
+  )
+
+install(
+  PROGRAMS nepomukcontroller.desktop
+  DESTINATION ${XDG_APPS_INSTALL_DIR})
+install(
+  PROGRAMS nepomukcontroller.desktop
+  DESTINATION ${AUTOSTART_INSTALL_DIR})
+install(
+  TARGETS nepomukcontroller
+  ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/nepomuk/controller/Messages.sh b/nepomuk/controller/Messages.sh
new file mode 100644
index 0000000..ea4aa1e
--- /dev/null
+++ b/nepomuk/controller/Messages.sh
@@ -0,0 +1,4 @@
+#! /usr/bin/env bash
+$EXTRACTRC `find . -name "*.ui" -o -name "*.kcfg"` >> rc.cpp
+$XGETTEXT `find . -name "*.cpp"` -o $podir/nepomukcontroller.pot
+rm -rf rc.cpp
diff --git a/nepomuk/controller/main.cpp b/nepomuk/controller/main.cpp
new file mode 100644
index 0000000..ddd9327
--- /dev/null
+++ b/nepomuk/controller/main.cpp
@@ -0,0 +1,53 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "systray.h"
+
+#include <KUniqueApplication>
+#include <KCmdLineArgs>
+#include <KAboutData>
+
+int main( int argc, char *argv[] )
+{
+    KAboutData aboutData( "nepomukcontroller",
+                          "nepomukcontroller",
+                          ki18n("Nepomuk Controller"),
+                          "1.0",
+                          ki18n("A small tool to monitor and control Nepomuk file indexing"),
+                          KAboutData::License_GPL,
+                          ki18n("(c) 2008-2011, Sebastian Trüg"),
+                          KLocalizedString(),
+                          "http://nepomuk.kde.org" );
+    aboutData.addAuthor(ki18n("Sebastian Trüg"),ki18n("Maintainer"), "trueg@kde.org");
+    aboutData.setProgramIconName( "nepomuk" );
+
+    KCmdLineArgs::init( argc, argv, &aboutData );
+    KCmdLineOptions options;
+    KCmdLineArgs::addCmdLineOptions( options );
+
+    if( KUniqueApplication::start() ) {
+        KUniqueApplication app;
+        app.setQuitOnLastWindowClosed(false);
+        KGlobal::locale()->insertCatalog( QLatin1String( "kcm_nepomuk" )); 
+        (void)new Nepomuk::SystemTray(0);
+        return app.exec();
+    }
+}
diff --git a/nepomuk/controller/nepomukcontroller.desktop b/nepomuk/controller/nepomukcontroller.desktop
new file mode 100644
index 0000000..28cc312
--- /dev/null
+++ b/nepomuk/controller/nepomukcontroller.desktop
@@ -0,0 +1,104 @@
+[Desktop Entry]
+Name=Nepomuk File Indexing Controller
+Name[bg]=Управление на файлово индексиране с Nepomuk
+Name[bs]=Nepomuk kontrolor indeksiranja datoteka
+Name[ca]=Controlador de l'indexador de fitxers Nepomuk
+Name[ca@valencia]=Controlador de l'indexador de fitxers Nepomuk
+Name[cs]=Správce indexování souborů Nepomuku
+Name[da]=Kontrol af Nepomuk filindeksering
+Name[de]=Steuerung der Nepomuk-Datei-Indizierung
+Name[es]=Controlador de indexado de archivos de Nepomuk
+Name[et]=Nepomuki failide indekseerimise kontroller
+Name[eu]=Nepomuken fitxategiak indexatzeko kontrolatzailea
+Name[fi]=Nepomukin tiedostojen indeksoinnin hallinta
+Name[fr]=Contrôleur d'indexation de fichiers de Nepomuk
+Name[he]=לוח בקרה לממפתח הקבצים  של Nepomuk 
+Name[hr]=Nepomukov upravljač za indeksiranje datoteka
+Name[hu]=Nepomuk fájlindexelő-felügyelő
+Name[ia]=Controlator de indicisation de file de Nepomuk
+Name[is]=Nepomuk skráaskráningarstýring
+Name[it]=Controllore dell'indicizzazione file di Nepomuk
+Name[kk]=Nepomuk файл индекстеу контроллері
+Name[km]=កម្មវិធី​ត្រួតពិនិត្យ​ការ​ដាក់​លិបិក្រម​ឯកសារ​របស់ Nepomuk
+Name[ko]=Nepomuk 파일 인덱싱 컨트롤러
+Name[lt]=Nepomuk failų indeksavimo valdiklis
+Name[lv]=Nepomuk failu indeksēšanas kontrolieris
+Name[nb]=Styring for Nepomuk filindeksering
+Name[nds]=Dateiindizeren för Nepomuk
+Name[nl]=Besturing door Nepomuk van bestandenindexering
+Name[nn]=Nepomuk filindekseringskontroll
+Name[pa]=ਨਿਪੋਮੁਕ ਫਾਇਲ ਇੰਡੈਕਸਿੰਗ ਕੰਟਰੋਲਰ
+Name[pl]=Kontroler indeksowania plików Nepomuk
+Name[pt]=Controlador de Indexação de Ficheiros do Nepomuk
+Name[pt_BR]=Controlador de indexação de arquivos do Nepomuk
+Name[ro]=Controlor al indexatorului de fișiere Nepomuk
+Name[ru]=Управление индексированием файлов в Nepomuk
+Name[sl]=Nadzorovalnik indeksiranja datotek Nepomuk
+Name[sr]=Непомуков управљач индексирањем фајлова
+Name[sr@ijekavian]=Непомуков управљач индексирањем фајлова
+Name[sr@ijekavianlatin]=Nepomukov upravljač indeksiranjem fajlova
+Name[sr@latin]=Nepomukov upravljač indeksiranjem fajlova
+Name[sv]=Nepomuk-styrning av filindexering
+Name[th]=ตัวควบคุมการทำดัชนีแฟ้มของ Nepomuk
+Name[tr]=Nepomuk Dosya İndeksleme Kontrolcüsü
+Name[ug]=Nepomuk ھۆججەت ئىندېكسلاش تىزگىنلىگۈچ
+Name[uk]=Засіб керування індексуванням файлів у Nepomuk
+Name[x-test]=xxNepomuk File Indexing Controllerxx
+Name[zh_CN]=Nepomuk 文件索引控制器
+Name[zh_TW]=Nepomuk 檔案索引控制器
+Comment=System tray icon to control the behaviour of the Nepomuk file indexer
+Comment[bg]=Икона в системния поднос за управление на файлово индексиране с Nepomuk
+Comment[bs]=Ikona u sistemskom uglu za kontrolu ponapanja Nepomuk indeksiranja datoteka
+Comment[ca]=Icona de la safata del sistema per controlar el comportament de l'indexador de fitxers Nepomuk
+Comment[ca@valencia]=Icona de la safata del sistema per controlar el comportament de l'indexador de fitxers Nepomuk
+Comment[cs]=Ikona v systémové části panelu pro ovládání chování indexeru souborů Nepomuk
+Comment[da]=Statusikon til at kontrollere opførsel af Nepomuk filindeksering
+Comment[de]=Symbol im Systemabschnitt der Kontrollleiste, um das Verhalten der Nepomuk-Datei-Indizierung zu steuern
+Comment[es]=Icono de la bandeja del sistema para controlar el comportamiento de la indexación de archivos de Nepomuk
+Comment[et]=Süsteemisalve ikoon Nepomuki failide indekseerimise käitumise juhtimiseks
+Comment[eu]=Sistema erretiluko ikonoa Nepomuken fitxategi indexatzailearen portaera kontrolatzeko
+Comment[fi]=Ilmoitusaluekuvake Nepomukin tiedostojen indeksoinnin hallintaan.
+Comment[fr]=Icône de la boite à miniature contrôlant le comportement de l'indexeur de fichier Nepomuk
+Comment[he]=צלמית במגש המערכת לשליטה על התנהגות הממפתח של Nepomuk
+Comment[hr]=Ikona u sistemskom bloku za upravljanje ponašanjem Nepomukovog indeksiranja datoteka
+Comment[hu]=Paneltálcaikon a Nepomuk fájlindexelő működésének felügyeléséhez
+Comment[ia]=Icone de tabuliero de systema per controlar le comportamento del indicisator de file de Nepomuk
+Comment[is]=Táknmynd í kerfisbakka til að stýra hegðun Nepomuk skráaskráningarstýringar
+Comment[it]=Icona nel vassoio di sistema che controlla il comportamento dell'indicizzatore di file Nepomuk
+Comment[kk]=Nepomuk файл индекстеуін басқару үшін жүйелік сөреде таңбашаны көрсету
+Comment[km]=រូបតំណាង​ថាស​ប្រព័ន្ធ​ ដើម្បី​ត្រួតពិនិត្យ​ឥរិយាបថ​របស់​កម្មវិធី​ដាក់​លិបិក្រម​ឯកសារ​របស់ Nepomuk
+Comment[ko]=Nepomuk 파일 인덱서 제어를 위한 시스템 트레이 아이콘
+Comment[lt]=Sisteminio dėklo ženkliukas Nepomuk failų indeksavimo elgsenos valdymui
+Comment[lv]=Sistēmas joslas ikona, ar kuru kontrolēt Nepomuk failu indeksētāja uzvedību
+Comment[nb]=Ikon i systemkurven som styrer hvordan Nepomuk filindeksering oppfører seg
+Comment[nds]=Systeemafsnitt-Lüttbild för de Kuntrull vun den Nepomuk-Dateiindizeerdeenst
+Comment[nl]=Pictogram in het systeemvak om het gedrag van bestandenindexering van Nepomuk te besturen
+Comment[nn]=Systemtrauikon for å kontrollera Nepomuk filindeksering
+Comment[pa]=ਨਿਪੋਮੁਕ ਫਾਇਲ ਇੰਡੈਕਸਰ ਦਾ ਰਵੱਈਆ ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਸਿਸਟਮ ਟਰੇ ਆਈਕਾਨ
+Comment[pl]=Ikona w tacce systemowej do kontroli zachowania indeksowania plików Nepomuk
+Comment[pt]=Ícone da bandeja para controlar o comportamento de indexação de ficheiros do Nepomuk
+Comment[pt_BR]=Ícone da área de notificação para controlar o comportamento de indexação de arquivos do Nepomuk
+Comment[ro]=Pictograma din tava de sistem pentru a controla comportamentul indexatorului de fișiere Nepomuk
+Comment[ru]=Значок в системном лотке для управления индексированием в Nepomuk
+Comment[sl]=Ikona v sistemski vrstici za nadzorovanje delovanja indeksirnika datotek Nepomuk
+Comment[sr]=Икона системске касете за управљање Непомуковим индексаром фајлова
+Comment[sr@ijekavian]=Икона системске касете за управљање Непомуковим индексаром фајлова
+Comment[sr@ijekavianlatin]=Ikona sistemske kasete za upravljanje Nepomukovim indeksarom fajlova
+Comment[sr@latin]=Ikona sistemske kasete za upravljanje Nepomukovim indeksarom fajlova
+Comment[sv]=Ikon i systembrickan för att styra beteendet hos Nepomuk-filindexering
+Comment[th]=ไอคอนบนถาดระบบสำหรับควบคุมตัวทำดัชนีแฟ้มสำหรับ Nepomuk
+Comment[tr]=Nepomuk dosya indeksleyicinin davranışını kontrol eden sistem çekmecesi simgesi
+Comment[ug]=سىستېما پەغەز سىنبەلگىسى Nepomuk ھۆججەت ئىندېكىسلىغۇچنىڭ ھەرىكىتىنى تىزگىنلەيدۇ
+Comment[uk]=Піктограма системного лотка для керування поведінкою служб індексування файлів Nepomuk
+Comment[x-test]=xxSystem tray icon to control the behaviour of the Nepomuk file indexerxx
+Comment[zh_CN]=控制 Nepomuk 文件索引器的系统托盘图标
+Comment[zh_TW]=控制 Nepomuk 檔案索引行為的系統匣圖示
+Icon=nepomuk
+Exec=nepomukcontroller
+Type=Application
+Categories=Qt;KDE;System;
+X-KDE-autostart-after=panel
+X-DBUS-StartupType=Unique
+X-KDE-StartupNotify=false
+X-KDE-UniqueApplet=true
+X-KDE-autostart-condition=nepomukserverrc:Basic Settings:Start Nepomuk:true
diff --git a/nepomuk/controller/systray.cpp b/nepomuk/controller/systray.cpp
new file mode 100644
index 0000000..920877b
--- /dev/null
+++ b/nepomuk/controller/systray.cpp
@@ -0,0 +1,188 @@
+/* This file is part of the KDE Project
+   Copyright (c) 2008-2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License version 2 as published by the Free Software Foundation.
+
+   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 Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#include "systray.h"
+#include "statuswidget.h"
+
+#include <KMenu>
+#include <KToggleAction>
+#include <KLocale>
+#include <KIcon>
+#include <KToolInvocation>
+#include <KActionCollection>
+#include <KStandardAction>
+
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusConnectionInterface>
+#include <QtDBus/QDBusServiceWatcher>
+
+#include <QApplication>
+#include <QDesktopWidget>
+
+
+Nepomuk::SystemTray::SystemTray( QObject* parent )
+    : KStatusNotifierItem( parent ),
+      m_service( 0 ),
+      m_suspendedManually( false ),
+      m_statusWidget( 0 )
+{
+    setCategory( SystemServices );
+    setIconByName( "nepomuk" );
+    setTitle( i18n( "Nepomuk File Indexing" ) );
+
+    // the status widget
+    connect(this, SIGNAL(activateRequested(bool,QPoint)), this, SLOT(slotActivateRequested()));
+
+    m_suspendResumeAction = new KToggleAction( i18n( "Suspend File Indexing" ), actionCollection() );
+    m_suspendResumeAction->setCheckedState( KGuiItem( i18n( "Suspend File Indexing" ) ) );
+    m_suspendResumeAction->setToolTip( i18n( "Suspend or resume the file indexer manually" ) );
+    connect( m_suspendResumeAction, SIGNAL( toggled( bool ) ),
+             this, SLOT( slotSuspend( bool ) ) );
+
+    KAction* configAction = new KAction( actionCollection() );
+    configAction->setText( i18n( "Configure File Indexing" ) );
+    configAction->setIcon( KIcon( "configure" ) );
+    connect( configAction, SIGNAL( triggered() ),
+             this, SLOT( slotConfigure() ) );
+
+    contextMenu()->addAction( m_suspendResumeAction );
+    contextMenu()->addAction( configAction );
+
+    // connect to the strigi service
+    m_service = new org::kde::nepomuk::Strigi( QLatin1String("org.kde.nepomuk.services.nepomukstrigiservice"),
+                                               QLatin1String("/nepomukstrigiservice"),
+                                               QDBusConnection::sessionBus(),
+                                               this );
+    m_serviceControl = new org::kde::nepomuk::ServiceControl( QLatin1String("org.kde.nepomuk.services.nepomukstrigiservice"),
+                                                              QLatin1String("/servicecontrol"),
+                                                              QDBusConnection::sessionBus(),
+                                                              this );
+    connect( m_service, SIGNAL( statusChanged() ), this, SLOT( slotUpdateStrigiStatus() ) );
+
+    // watch for the strigi service to come up and go down
+    QDBusServiceWatcher* dbusServiceWatcher = new QDBusServiceWatcher( m_service->service(),
+                                                                       QDBusConnection::sessionBus(),
+                                                                       QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration,
+                                                                       this );
+    connect( dbusServiceWatcher, SIGNAL( serviceRegistered( QString ) ),
+             this, SLOT( slotUpdateStrigiStatus()) );
+    connect( dbusServiceWatcher, SIGNAL( serviceUnregistered( QString ) ),
+             this, SLOT( slotUpdateStrigiStatus()) );
+
+    slotUpdateStrigiStatus();
+}
+
+
+Nepomuk::SystemTray::~SystemTray()
+{
+}
+
+
+void Nepomuk::SystemTray::slotUpdateStrigiStatus()
+{
+    // make sure we do not update the systray icon all the time
+
+    ItemStatus newStatus = status();
+    const bool strigiServiceInitialized =
+            QDBusConnection::sessionBus().interface()->isServiceRegistered(m_service->service()) &&
+            m_serviceControl->isInitialized();
+
+    // a manually suspended service should not be passive
+    if( strigiServiceInitialized )
+        newStatus = m_service->isIndexing() || m_suspendedManually ? Active : Passive;
+    else
+        newStatus = Passive;
+    if ( newStatus != status() ) {
+        setStatus( newStatus );
+    }
+
+    QString statusString;
+    if( strigiServiceInitialized )
+        statusString = m_service->userStatusString();
+    else
+        statusString = i18n("Nepomuk File Indexing Service not running");
+    if ( statusString != m_prevStatus ) {
+        m_prevStatus = statusString;
+        setToolTip("nepomuk", i18n("Search Service"), statusString );
+    }
+
+    m_suspendResumeAction->setEnabled( strigiServiceInitialized );
+    m_suspendResumeAction->setChecked( m_service->isSuspended() );
+}
+
+
+void Nepomuk::SystemTray::slotConfigure()
+{
+    QStringList args;
+    args << "kcm_nepomuk";
+    KToolInvocation::kdeinitExec("kcmshell4", args);
+}
+
+
+void Nepomuk::SystemTray::slotSuspend( bool suspended )
+{
+    m_suspendedManually = suspended;
+    if( suspended )
+        m_service->suspend();
+    else
+        m_service->resume();
+}
+
+
+// from kdialog.cpp since KDialog::centerOnScreen will simply do nothing on X11!
+static QRect screenRect( QWidget *widget, int screen )
+{
+    QDesktopWidget *desktop = QApplication::desktop();
+    KConfig gc( "kdeglobals", KConfig::NoGlobals );
+    KConfigGroup cg(&gc, "Windows" );
+    if ( desktop->isVirtualDesktop() &&
+         cg.readEntry( "XineramaEnabled", true ) &&
+         cg.readEntry( "XineramaPlacementEnabled", true ) ) {
+
+        if ( screen < 0 || screen >= desktop->numScreens() ) {
+            if ( screen == -1 )
+                screen = desktop->primaryScreen();
+            else if ( screen == -3 )
+                screen = desktop->screenNumber( QCursor::pos() );
+            else
+                screen = desktop->screenNumber( widget );
+        }
+
+        return desktop->availableGeometry( screen );
+    } else
+        return desktop->geometry();
+}
+
+void Nepomuk::SystemTray::slotActivateRequested()
+{
+    if(!m_statusWidget)
+        m_statusWidget = new Nepomuk::StatusWidget();
+
+    if(!m_statusWidget->isVisible()) {
+        m_statusWidget->show();
+
+        const QRect rect = screenRect( 0, -3 );
+        m_statusWidget->move( rect.center().x() - m_statusWidget->width() / 2,
+                              rect.center().y() - m_statusWidget->height() / 2 );
+    }
+    else {
+        m_statusWidget->hide();
+    }
+}
+
+#include "systray.moc"
diff --git a/nepomuk/controller/systray.h b/nepomuk/controller/systray.h
new file mode 100644
index 0000000..3e2adb6
--- /dev/null
+++ b/nepomuk/controller/systray.h
@@ -0,0 +1,62 @@
+/* This file is part of the KDE Project
+   Copyright (c) 2008-2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License version 2 as published by the Free Software Foundation.
+
+   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 Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _NEPOMUK_STRIGI_SYSTRAY_H_
+#define _NEPOMUK_STRIGI_SYSTRAY_H_
+
+#include <KStatusNotifierItem>
+#include "strigiserviceinterface.h"
+#include "servicecontrol.h"
+
+class KToggleAction;
+
+namespace Nepomuk {
+
+    class StrigiService;
+    class StatusWidget;
+
+    class SystemTray : public KStatusNotifierItem
+    {
+        Q_OBJECT
+
+    public:
+        SystemTray( QObject* parent );
+        ~SystemTray();
+
+    private Q_SLOTS:
+        void slotUpdateStrigiStatus();
+        void slotConfigure();
+        void slotSuspend( bool suspended );
+
+        void slotActivateRequested();
+
+    private:
+        KToggleAction* m_suspendResumeAction;
+
+        org::kde::nepomuk::Strigi* m_service;
+        org::kde::nepomuk::ServiceControl* m_serviceControl;
+        bool m_suspendedManually;
+
+        StatusWidget* m_statusWidget;
+
+        // used to prevent endless status updates
+        QString m_prevStatus;
+    };
+}
+
+#endif
diff --git a/nepomuk/interfaces/CMakeLists.txt b/nepomuk/interfaces/CMakeLists.txt
index db20fe6..2e14346 100644
--- a/nepomuk/interfaces/CMakeLists.txt
+++ b/nepomuk/interfaces/CMakeLists.txt
@@ -9,11 +9,11 @@ install(FILES
   org.kde.nepomuk.Storage.xml
   org.kde.nepomuk.QueryService.xml
   org.kde.nepomuk.Query.xml
-  org.kde.nepomuk.RemovableStorage.xml
   org.kde.nepomuk.BackupSync.xml
   org.kde.nepomuk.services.nepomukbackupsync.backupmanager.xml
   org.kde.nepomuk.services.nepomukbackupsync.syncmanager.xml
-  org.kde.nepomuk.services.nepomukbackupsync.identifier.xml
-  org.kde.nepomuk.services.nepomukbackupsync.merger.xml
+  org.kde.nepomuk.DataManagement.xml
+  org.kde.nepomuk.ResourceWatcher.xml
+  org.kde.nepomuk.ResourceWatcherConnection.xml
   DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}
   )
diff --git a/nepomuk/interfaces/org.kde.nepomuk.DataManagement.xml b/nepomuk/interfaces/org.kde.nepomuk.DataManagement.xml
new file mode 100644
index 0000000..0172373
--- /dev/null
+++ b/nepomuk/interfaces/org.kde.nepomuk.DataManagement.xml
@@ -0,0 +1,121 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.nepomuk.DataManagement">
+    <method name="addProperty">
+      <arg name="resources" type="as" direction="in"/>
+      <arg name="property" type="s" direction="in"/>
+      <arg name="values" type="av" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="setProperty">
+      <arg name="resources" type="as" direction="in"/>
+      <arg name="property" type="s" direction="in"/>
+      <arg name="values" type="av" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="removeProperty">
+      <arg name="resources" type="as" direction="in"/>
+      <arg name="property" type="s" direction="in"/>
+      <arg name="values" type="av" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="removeProperties">
+      <arg name="resources" type="as" direction="in"/>
+      <arg name="properties" type="as" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="createResource">
+      <arg type="s" direction="out"/>
+      <arg name="types" type="as" direction="in"/>
+      <arg name="label" type="s" direction="in"/>
+      <arg name="description" type="s" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="removeResources">
+      <arg name="resources" type="as" direction="in"/>
+      <arg name="flags" type="i" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="removeDataByApplication">
+      <arg name="resources" type="as" direction="in"/>
+      <arg name="flags" type="i" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="removeDataByApplication">
+      <arg name="flags" type="i" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="storeResources">
+      <arg name="resources" type="a(sa{sv})" direction="in"/>
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QList&lt;Nepomuk::SimpleResource&gt;"/>
+      <arg name="identificationMode" type="i" direction="in"/>
+      <arg name="flags" type="i" direction="in"/>
+      <arg name="additionalMetadata" type="a{sv}" direction="in"/>
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In3" value="Nepomuk::PropertyHash"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="importResources">
+      <arg name="url" type="s" direction="in"/>
+      <arg name="serialization" type="s" direction="in"/>
+      <arg name="identificationMode" type="i" direction="in"/>
+      <arg name="flags" type="i" direction="in"/>
+      <arg name="additionalMetadata" type="a{sv}" direction="in"/>
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In4" value="Nepomuk::PropertyHash"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="importResources">
+      <arg name="url" type="s" direction="in"/>
+      <arg name="serialization" type="s" direction="in"/>
+      <arg name="identificationMode" type="i" direction="in"/>
+      <arg name="flags" type="i" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="mergeResources">
+      <arg name="resource1" type="s" direction="in"/>
+      <arg name="resource2" type="s" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="describeResources">
+      <arg name="resources" type="as" direction="in"/>
+      <arg name="includeSubResources" type="b" direction="in"/>
+      <arg type="a(sa{sv})" direction="out"/>
+      <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QList&lt;Nepomuk::SimpleResource&gt;"/>
+    </method>
+    <method name="setProperty">
+      <arg name="resource" type="s" direction="in"/>
+      <arg name="property" type="s" direction="in"/>
+      <arg name="value" type="v" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="addProperty">
+      <arg name="resource" type="s" direction="in"/>
+      <arg name="property" type="s" direction="in"/>
+      <arg name="value" type="v" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="removeProperty">
+      <arg name="resource" type="s" direction="in"/>
+      <arg name="property" type="s" direction="in"/>
+      <arg name="value" type="v" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="removeProperties">
+      <arg name="resource" type="s" direction="in"/>
+      <arg name="property" type="s" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="createResource">
+      <arg type="s" direction="out"/>
+      <arg name="type" type="s" direction="in"/>
+      <arg name="label" type="s" direction="in"/>
+      <arg name="description" type="s" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+    <method name="removeResources">
+      <arg name="resource" type="s" direction="in"/>
+      <arg name="flags" type="i" direction="in"/>
+      <arg name="app" type="s" direction="in"/>
+    </method>
+  </interface>
+</node>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.Query.xml b/nepomuk/interfaces/org.kde.nepomuk.Query.xml
index af57cb8..0e444e7 100644
--- a/nepomuk/interfaces/org.kde.nepomuk.Query.xml
+++ b/nepomuk/interfaces/org.kde.nepomuk.Query.xml
@@ -18,6 +18,10 @@
     <signal name="entriesRemoved">
       <arg name="entries" type="as" />
     </signal>
+    <signal name="entriesRemoved">
+      <arg name="entries" type="a(sda{s(isss)}s)" />
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QList&lt;Nepomuk::Query::Result&gt;" />
+    </signal>
     <signal name="resultCount">
       <arg name="count" type="i" />
     </signal>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.RemovableStorage.xml b/nepomuk/interfaces/org.kde.nepomuk.RemovableStorage.xml
deleted file mode 100644
index 38a29ea..0000000
--- a/nepomuk/interfaces/org.kde.nepomuk.RemovableStorage.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
-          "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
-<node>
-  <interface name="org.kde.nepomuk.RemovableStorage">
-    <method name="resourceUriFromLocalFileUrl">
-      <arg type="s" direction="out"/>
-      <arg name="url" type="s" direction="in"/>
-    </method>
-    <method name="currentlyMountedAndIndexed">
-      <arg type="as" direction="out"/>
-    </method>
-  </interface>
-</node>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.ResourceWatcher.xml b/nepomuk/interfaces/org.kde.nepomuk.ResourceWatcher.xml
new file mode 100644
index 0000000..7f41444
--- /dev/null
+++ b/nepomuk/interfaces/org.kde.nepomuk.ResourceWatcher.xml
@@ -0,0 +1,14 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.nepomuk.ResourceWatcher">
+    <method name="stopWatcher">
+      <arg name="objectName" type="s" direction="in"/>
+    </method>
+    <method name="watch">
+      <arg name="resources" type="as" direction="in"/>
+      <arg name="properties" type="as" direction="in"/>
+      <arg name="types" type="as" direction="in"/>
+      <arg name="queryobject" type="o" direction="out" />
+    </method>
+  </interface>
+</node>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.ResourceWatcherConnection.xml b/nepomuk/interfaces/org.kde.nepomuk.ResourceWatcherConnection.xml
new file mode 100644
index 0000000..8b175bc
--- /dev/null
+++ b/nepomuk/interfaces/org.kde.nepomuk.ResourceWatcherConnection.xml
@@ -0,0 +1,32 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.nepomuk.ResourceWatcherConnection">
+    <signal name="resourceCreated">
+      <arg name="uri" type="s" direction="out"/>
+      <arg name="types" type="as" direction="out"/>
+    </signal>
+    <signal name="resourceRemoved">
+      <arg name="uri" type="s" direction="out"/>
+      <arg name="types" type="as" direction="out"/>
+    </signal>
+    <signal name="resourceTypeAdded">
+      <arg name="resUri" type="s" direction="out"/>
+      <arg name="type" type="s" direction="out"/>
+    </signal>
+    <signal name="resourceTypeRemoved">
+      <arg name="resUri" type="s" direction="out"/>
+      <arg name="type" type="s" direction="out"/>
+    </signal>
+    <signal name="propertyAdded">
+      <arg name="resource" type="s" direction="out"/>
+      <arg name="property" type="s" direction="out"/>
+      <arg name="value" type="v" direction="out"/>
+    </signal>
+    <signal name="propertyRemoved">
+      <arg name="resource" type="s" direction="out"/>
+      <arg name="property" type="s" direction="out"/>
+      <arg name="value" type="v" direction="out"/>
+    </signal>
+    <method name="close" />
+  </interface>
+</node>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.Strigi.xml b/nepomuk/interfaces/org.kde.nepomuk.Strigi.xml
index 4725958..44b736d 100644
--- a/nepomuk/interfaces/org.kde.nepomuk.Strigi.xml
+++ b/nepomuk/interfaces/org.kde.nepomuk.Strigi.xml
@@ -32,16 +32,6 @@
     <method name="indexFile">
       <arg name="path" type="s" direction="in" />
     </method>
-    <method name="analyzeResource" >
-      <arg name="uri" direction="in" type="s" />
-      <arg name="lastModificationDate" direction="in" type="u" />
-      <arg name="data" direction="in" type="ay" />
-    </method>
-    <method name="analyzeResourceFromTempFileAndDeleteTempFile" >
-      <arg name="uri" direction="in" type="s" />
-      <arg name="lastModificationDate" direction="in" type="u" />
-      <arg name="tmpFileName" direction="in" type="s" />
-    </method>
     <method name="userStatusString">
       <arg type="s" direction="out" />
     </method>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.backupmanager.xml b/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.backupmanager.xml
index e7d3cc1..85b2d12 100644
--- a/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.backupmanager.xml
+++ b/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.backupmanager.xml
@@ -4,10 +4,6 @@
     <method name="backup">
         <arg name="url" type="s" direction="in"/>
     </method>
-    <method name="restore">
-        <arg type="i" direction="out"/>
-        <arg name="url" type="s" direction="in"/>
-    </method>
     <signal name="backupDone">
     </signal>
   </interface>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.identifier.xml b/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.identifier.xml
deleted file mode 100644
index 191b685..0000000
--- a/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.identifier.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
-<node>
-  <interface name="org.kde.nepomuk.services.nepomukbackupsync.Identifier">
-    <signal name="identified">
-      <arg name="id" type="i" direction="out"/>
-      <arg name="oldUri" type="s" direction="out"/>
-      <arg name="newUri" type="s" direction="out"/>
-    </signal>
-    <signal name="identificationDone">
-        <arg name="id" type="i" direction="out"/>
-        <arg name="unidentified" type="i" direction="out"/>
-    </signal>
-    <signal name="notIdentified">
-      <arg name="id" type="i" direction="out"/>
-      <arg name="serializedStatements" type="s" direction="out"/>
-    </signal>
-    <signal name="completed">
-        <arg name="id" type="i" direction="out"/>
-        <arg name="progress" type="i" direction="out"/>
-    </signal>
-    <method name="identify">
-      <arg type="b" direction="out"/>
-      <arg name="id" type="i" direction="in"/>
-      <arg name="oldUri" type="s" direction="in"/>
-      <arg name="newUri" type="s" direction="in"/>
-    </method>
-    <method name="ignore">
-        <arg type="b" direction="out"/>
-        <arg name="id" type="i" direction="in"/>
-        <arg name="url" type="s" direction="in"/>
-        <arg name="ignoreSubDirectories" type="b" direction="in"/>
-    </method>
-    <method name="ignoreAll">
-        <arg name="id" type="i" direction="in"/>
-    </method>
-    <method name="completeIdentification">
-        <arg name="id" type="i" direction="in"/>
-    </method>
-  </interface>
-</node>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.merger.xml b/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.merger.xml
deleted file mode 100644
index 71c7812..0000000
--- a/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.merger.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
-<node>
-    <interface name="org.kde.nepomuk.services.nepomukbackupsync.Merger">
-    <signal name="completed">
-      <arg name="percent" type="i" direction="out"/>
-    </signal>
-    <signal name="multipleMerge">
-      <arg name="uri" type="s" direction="out"/>
-      <arg name="prop" type="s" direction="out"/>
-    </signal>
-  </interface>
-</node>
diff --git a/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.syncmanager.xml b/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.syncmanager.xml
index 8db092e..f8a87f2 100644
--- a/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.syncmanager.xml
+++ b/nepomuk/interfaces/org.kde.nepomuk.services.nepomukbackupsync.syncmanager.xml
@@ -1,10 +1,6 @@
 <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 <node>
     <interface name="org.kde.nepomuk.services.nepomukbackupsync.SyncManager">
-    <method name="sync">
-      <arg type="i" direction="out"/>
-      <arg name="url" type="s" direction="in"/>
-    </method>
     <method name="createSyncFile">
       <arg name="url" type="s" direction="in"/>
       <arg name="startTime" type="s" direction="in"/>
diff --git a/nepomuk/kcm/kcm_nepomuk.desktop b/nepomuk/kcm/kcm_nepomuk.desktop
index 75f6ef0..f03686b 100644
--- a/nepomuk/kcm/kcm_nepomuk.desktop
+++ b/nepomuk/kcm/kcm_nepomuk.desktop
@@ -17,6 +17,7 @@ Name[be@latin]=Pošuk
 Name[bg]=Настолно търсене
 Name[bn]=ডেস্কটপ সন্ধান
 Name[bn_IN]=ডেস্কটপে অনুসন্ধান
+Name[bs]=Pretraga površi
 Name[ca]=Cerca a l'escriptori
 Name[ca@valencia]=Cerca a l'escriptori
 Name[cs]=Vyhledávací služby
@@ -33,7 +34,7 @@ Name[fi]=Työpöytähaku
 Name[fr]=Rechercher sur le bureau
 Name[fy]=Buroblêd sykaksje
 Name[ga]=Cuardach Deisce
-Name[gl]=Procura no escritorio
+Name[gl]=Busca no escritorio
 Name[gu]=ડેસ્કટોપ શોધ
 Name[he]=חיפוש בשולחן העבודה
 Name[hi]=डेस्कटॉप खोज
@@ -81,6 +82,7 @@ Name[te]=డెస్‍క్ టాప్ శోధన
 Name[tg]=Ҷустуҷӯи мизи корӣ
 Name[th]=ค้นหาผ่านพื้นที่ทำงาน
 Name[tr]=Masaüstü Araması
+Name[ug]=ئۈستەلئۈستىدە ئىزدەش
 Name[uk]=Стільничний пошук
 Name[wa]=Cweraedje sol sicribanne
 Name[x-test]=xxDesktop Searchxx
@@ -94,6 +96,7 @@ Comment[be@latin]=Nałady servera „Nepomuk”/„Strigi”
 Comment[bg]=Настройване на Nepomuk/Strigi
 Comment[bn]=নেপোমুক/স্ট্রিগি সার্ভার কনফিগারেশন
 Comment[bn_IN]=Nepomuk/Strigi সার্ভার কনফিগারেশন
+Comment[bs]=Postava servera Nepomuka/Strigija
 Comment[ca]=Configuració del servidor Nepomuk/Strigi
 Comment[ca@valencia]=Configuració del servidor Nepomuk/Strigi
 Comment[cs]=Nastavení serveru Strigi/Nepomuk
@@ -130,7 +133,7 @@ Comment[ko]=Nepomuk/Strigi 서버 설정
 Comment[ku]=Veavakirinên Pêşkêşkara Nepomuk/Strigi
 Comment[lt]=Nepomuk/Strigi serverio konfigūravimas
 Comment[lv]=Nepomuk/Strigi servera konfigurācija
-Comment[mai]=नेपोमक/स्ट्रीगी सर्वर विन्यास
+Comment[mai]=नेपोमक/स्ट्रीगी सर्वर विन्यस्तेशन
 Comment[mk]=Конфигурација на Nepomuk/Strigi сервер
 Comment[ml]=നെപ്പോമുക്ക്/സ്ട്രിഗി സെര്‍വര്‍ ക്രമീകരണം
 Comment[mr]=Nepomuk/Strigi सर्वर संयोजना
@@ -159,6 +162,7 @@ Comment[te]=Nepomuk/Strigi సేవిక ఆకృతీకరణ
 Comment[tg]=Танзимоти хидматгоҳи Nepomuk/Strigi
 Comment[th]=ปรับแต่งบริการ Nepomuk/Strigi
 Comment[tr]=Nepomuk/Strigi Sunucu Yapılandırması
+Comment[ug]=Nepomuk/Strigi مۇلازىمېتىر سەپلىمىسى
 Comment[uk]=Налаштування сервера Nepomuk/Strigi
 Comment[uz]=Nepomuk/Strigi serverini moslash
 Comment[uz@cyrillic]=Nepomuk/Strigi серверини мослаш
diff --git a/nepomuk/kcm/nepomukconfigwidget.ui b/nepomuk/kcm/nepomukconfigwidget.ui
index 3e1d255..83efa4c 100644
--- a/nepomuk/kcm/nepomukconfigwidget.ui
+++ b/nepomuk/kcm/nepomukconfigwidget.ui
@@ -8,7 +8,7 @@
     <number>0</number>
    </property>
    <item>
-    <widget class="KTabWidget" name="tabWidget">
+    <widget class="KTabWidget" name="m_mainTabWidget">
      <property name="currentIndex">
       <number>0</number>
      </property>
@@ -77,6 +77,9 @@
                     <bold>true</bold>
                    </font>
                   </property>
+                  <property name="text">
+                   <string notr="true">KSqueezedTextLabel</string>
+                  </property>
                  </widget>
                 </item>
                </layout>
@@ -151,6 +154,9 @@
                 <bold>true</bold>
                </font>
               </property>
+              <property name="text">
+               <string notr="true">KSqueezedTextLabel</string>
+              </property>
              </widget>
             </item>
            </layout>
@@ -207,7 +213,7 @@
                </font>
               </property>
               <property name="text">
-               <string/>
+               <string notr="true">KSqueezedTextLabel</string>
               </property>
              </widget>
             </item>
@@ -221,14 +227,52 @@
            </layout>
           </item>
           <item>
-           <widget class="QCheckBox" name="m_checkIndexRemovableMedia">
-            <property name="toolTip">
-             <string>Index the files on removable media like USB sticks when they are mounted</string>
-            </property>
-            <property name="text">
-             <string>Index files on removable media</string>
-            </property>
-           </widget>
+           <layout class="QFormLayout" name="formLayout_3">
+            <item row="0" column="0">
+             <widget class="QLabel" name="label_11">
+              <property name="text">
+               <string>Removable media handling:</string>
+              </property>
+              <property name="buddy">
+               <cstring>m_comboRemovableMediaHandling</cstring>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="KComboBox" name="m_comboRemovableMediaHandling">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                <horstretch>1</horstretch>
+                <verstretch>0</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="toolTip">
+               <string comment="@info:tooltip">Indexing of files on removable media</string>
+              </property>
+              <property name="whatsThis">
+               <string comment="@info:whatsthis">&lt;para&gt;Nepomuk can index files on removable device like USB keys or external hard-disks for fast desktop searches.&lt;/para&gt;
+&lt;para&gt;By default no files are indexed. Here this behaviour can be changed to one of two options:&lt;/para&gt;
+&lt;para&gt;&lt;list&gt;&lt;item&gt;&lt;interface&gt;Index files on all removable devices&lt;/interface&gt; - Files on removable media are indexed as soon as the medium is mounted. Caution: this does not include media which have been rejected via the second option.&lt;/item&gt;
+&lt;item&gt;&lt;interface&gt;Ask individually when newly mounted&lt;/interface&gt; - The user will be asked to decide if files on the newly mounted medium should be indexed or not. Once decided Nepomuk will not ask again.&lt;/item&gt;&lt;/list&gt;&lt;/para&gt;</string>
+              </property>
+              <item>
+               <property name="text">
+                <string>Ignore all removable media</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>Index files on all removable devices</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>Ask individually when newly mounted</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+           </layout>
           </item>
          </layout>
         </widget>
@@ -322,7 +366,7 @@
                  </font>
                 </property>
                 <property name="text">
-                 <string/>
+                 <string notr="true">KSqueezedTextLabel</string>
                 </property>
                </widget>
               </item>
diff --git a/nepomuk/kcm/nepomukserverkcm.cpp b/nepomuk/kcm/nepomukserverkcm.cpp
index 97c9421..1a8d456 100644
--- a/nepomuk/kcm/nepomukserverkcm.cpp
+++ b/nepomuk/kcm/nepomukserverkcm.cpp
@@ -104,7 +104,7 @@ namespace {
     enum BackupFrequency {
         DisableAutomaticBackups = 0,
         DailyBackup = 1,
-        WeeklyBackup = 2
+        WeeklyBackup = 2,
     };
 
 
@@ -170,7 +170,8 @@ Nepomuk::ServerConfigModule::ServerConfigModule( QWidget* parent, const QVariant
                  this, SLOT( changed() ) );
         connect( m_sliderMemoryUsage, SIGNAL( valueChanged(int) ),
                  this, SLOT( changed() ) );
-        connect( m_checkIndexRemovableMedia, SIGNAL( toggled(bool) ),
+        connect( m_comboRemovableMediaHandling, SIGNAL( activated(int)
+                                                        ),
                  this, SLOT( changed() ) );
         connect( m_spinMaxResults, SIGNAL( valueChanged( int ) ),
                  this, SLOT( changed() ) );
@@ -216,6 +217,11 @@ Nepomuk::ServerConfigModule::ServerConfigModule( QWidget* parent, const QVariant
         // update backup status whenever manual backups are created
         KDirWatch::self()->addDir( KStandardDirs::locateLocal( "data", "nepomuk/backupsync/backups/" ) );
         connect( KDirWatch::self(), SIGNAL(dirty(QString)), this, SLOT(updateBackupStatus()) );
+
+        // args[0] can be the page index allowing to open the config with a specific page
+        if(args.count() > 0 && args[0].toInt() < m_mainTabWidget->count()) {
+            m_mainTabWidget->setCurrentIndex(args[0].toInt());
+        }
     }
     else {
         QLabel* label = new QLabel( i18n( "The Nepomuk installation is not complete. No Nepomuk settings can be provided." ) );
@@ -250,7 +256,11 @@ void Nepomuk::ServerConfigModule::load()
     m_indexFolderSelectionDialog->setFolders( strigiConfig.group( "General" ).readPathEntry( "folders", defaultFolders() ),
                                               strigiConfig.group( "General" ).readPathEntry( "exclude folders", QStringList() ) );
     m_indexFolderSelectionDialog->setExcludeFilters( strigiConfig.group( "General" ).readEntry( "exclude filters", Nepomuk::defaultExcludeFilterList() ) );
-    m_checkIndexRemovableMedia->setChecked( strigiConfig.group( "General" ).readEntry( "index newly mounted", false ) );
+
+    const bool indexNewlyMounted = strigiConfig.group( "RemovableMedia" ).readEntry( "index newly mounted", false );
+    const bool askIndividually = strigiConfig.group( "RemovableMedia" ).readEntry( "ask user", false );
+    // combobox items: 0 - ignore, 1 - index all, 2 - ask user
+    m_comboRemovableMediaHandling->setCurrentIndex(int(indexNewlyMounted) + int(askIndividually));
 
     groupBox->setEnabled(m_checkEnableNepomuk->isChecked());
 
@@ -321,7 +331,10 @@ void Nepomuk::ServerConfigModule::save()
     strigiConfig.group( "General" ).writePathEntry( "exclude folders", excludeFolders );
     strigiConfig.group( "General" ).writeEntry( "exclude filters", m_indexFolderSelectionDialog->excludeFilters() );
     strigiConfig.group( "General" ).writeEntry( "index hidden folders", m_indexFolderSelectionDialog->indexHiddenFolders() );
-    strigiConfig.group( "General" ).writeEntry( "index newly mounted", m_checkIndexRemovableMedia->isChecked() );
+
+    // combobox items: 0 - ignore, 1 - index all, 2 - ask user
+    strigiConfig.group( "RemovableMedia" ).writeEntry( "index newly mounted", m_comboRemovableMediaHandling->currentIndex() > 0 );
+    strigiConfig.group( "RemovableMedia" ).writeEntry( "ask user", m_comboRemovableMediaHandling->currentIndex() == 2 );
 
 
     // 3. update kio_nepomuksearch config
diff --git a/nepomuk/kcm/statuswidget.cpp b/nepomuk/kcm/statuswidget.cpp
index b198433..4798d7f 100644
--- a/nepomuk/kcm/statuswidget.cpp
+++ b/nepomuk/kcm/statuswidget.cpp
@@ -41,6 +41,7 @@ Nepomuk::StatusWidget::StatusWidget( QWidget* parent )
       m_updatingJobCnt( 0 ),
       m_updateRequested( false )
 {
+    KGlobal::locale()->insertCatalog("kcm_nepomuk");
     setupUi( mainWidget() );
 
     setCaption( m_title->text() );
diff --git a/nepomuk/kioslaves/common/resourcestat.cpp b/nepomuk/kioslaves/common/resourcestat.cpp
index bbd2c4a..53c2f1a 100644
--- a/nepomuk/kioslaves/common/resourcestat.cpp
+++ b/nepomuk/kioslaves/common/resourcestat.cpp
@@ -171,7 +171,7 @@ KUrl Nepomuk::convertRemovableMediaFileUrl( const KUrl& url, bool evenMountIfNec
          ( storage->isAccessible() ||
            ( evenMountIfNecessary && Nepomuk::mountAndWait( storage ) ) ) ) {
         kDebug() << "converted:" << KUrl( storage->filePath() + QLatin1String( "/" ) + url.path() );
-        return storage->filePath() + QLatin1String( "/" ) + url.path();
+        return QString( storage->filePath() + QLatin1String( "/" ) + url.path() );
     }
     else {
         return KUrl();
diff --git a/nepomuk/kioslaves/search/CMakeLists.txt b/nepomuk/kioslaves/search/CMakeLists.txt
index 887c735..dd12acf 100644
--- a/nepomuk/kioslaves/search/CMakeLists.txt
+++ b/nepomuk/kioslaves/search/CMakeLists.txt
@@ -10,6 +10,7 @@ include_directories(
   ${KDE4_KIO_INCLUDES}
   ${SOPRANO_INCLUDE_DIR}
   ${NEPOMUK_INCLUDE_DIR}
+  ${CMAKE_CURRENT_BINARY_DIR}
   ${nepomuk_kio_slaves_SOURCE_DIR}/common
 )
 
@@ -21,6 +22,8 @@ set(kio_nepomuksearch_PART_SRCS
   ../common/resourcestat.cpp
 )
 
+soprano_add_ontology(kio_nepomuksearch_PART_SRCS ${nepomuk_ontologies_SOURCE_DIR}/kext.trig "KExt" "Nepomuk::Vocabulary" "trig")
+
 kde4_add_plugin(kio_nepomuksearch
   ${kio_nepomuksearch_PART_SRCS})
 
diff --git a/nepomuk/kioslaves/search/kdedmodule/CMakeLists.txt b/nepomuk/kioslaves/search/kdedmodule/CMakeLists.txt
index 86c40f5..807dc0c 100644
--- a/nepomuk/kioslaves/search/kdedmodule/CMakeLists.txt
+++ b/nepomuk/kioslaves/search/kdedmodule/CMakeLists.txt
@@ -14,6 +14,8 @@ set(nepomuksearchmodule_SRCS
   ../../common/timelinetools.cpp
 )
 
+soprano_add_ontology(nepomuksearchmodule_SRCS ${nepomuk_ontologies_SOURCE_DIR}/kext.trig "KExt" "Nepomuk::Vocabulary" "trig")
+
 set_source_files_properties(
   ${nepomuk_kio_slaves_SOURCE_DIR}/../interfaces/org.kde.nepomuk.Query.xml
   PROPERTIES INCLUDE "nepomuk/result.h")
diff --git a/nepomuk/kioslaves/search/kdedmodule/nepomuksearchmodule.desktop b/nepomuk/kioslaves/search/kdedmodule/nepomuksearchmodule.desktop
index bbd71f4..a3fbcc9 100644
--- a/nepomuk/kioslaves/search/kdedmodule/nepomuksearchmodule.desktop
+++ b/nepomuk/kioslaves/search/kdedmodule/nepomuksearchmodule.desktop
@@ -10,6 +10,7 @@ Name[ar]=وحدة بحث نبومك
 Name[ast]=Módulu de gueta Nepomuk
 Name[bg]=Модул за търсене Nepomuk
 Name[bn]=নেপোমুক সন্ধান মডিউল
+Name[bs]=Pretraživački modul Nepomuka
 Name[ca]=Mòdul de cerques del Nepomuk
 Name[ca@valencia]=Mòdul de cerques del Nepomuk
 Name[cs]=Vyhledávací modul Nepomuk
@@ -39,7 +40,6 @@ Name[kn]=ನೆಪೋಮುಕ್ ಹುಡುಕು ಘಟಕ
 Name[ko]=Nepomuk 검색 모듈
 Name[lt]=Nepomuk paieškos modulis
 Name[lv]=Nepomuk meklēšanas modulis
-Name[mai]=नेपोमक खोज मोड्यूल
 Name[mk]=Модул на Непомук за пребарување
 Name[ml]=നെപ്പോമുക്ക് തെരച്ചില്‍ മൊഡ്യൂള്‍
 Name[nb]=Nepomuk søkemodul
@@ -63,6 +63,7 @@ Name[sv]=Nepomuk-sökmodul
 Name[tg]=Хидматҳои ҷустуҷӯии Nepomuk
 Name[th]=มอดูลค้นหาของบริการ Neomuk
 Name[tr]=Nepomuk Arama Modülü
+Name[ug]=Nepomuk ئىزدەش بۆلىكى
 Name[uk]=Модуль пошуку Nepomuk
 Name[wa]=Module di cweraedje Nepomuk
 Name[x-test]=xxNepomuk Search Modulexx
@@ -71,6 +72,8 @@ Name[zh_TW]=Nepomuk 搜尋模組
 Comment=Helper module for KIO to ensure automatic updates of nepomuksearch listings.
 Comment[ar]=الوحدة المساعدة لدخل وخرج كدي لضمان التحديث التلقائي للوائح بحث نِبوموك
 Comment[ast]=Módulu auxiliar de KIO p'asegurar l'anovamientu automáticu de llistaos de guetes con Nepomuk.
+Comment[bg]=Помощен модул за KIO осигуряващ автоматично обновяване на списък с търсени резултати
+Comment[bs]=Pomoćni modul za K‑U/I koji obezbeđuje automatsko ažuriranje spiskova Nepomukove pretrage.
 Comment[ca]=Mòdul ajudant per al KIO que assegura les actualitzacions automàtiques dels llistats del «nepomuksearch».
 Comment[ca@valencia]=Mòdul ajudant per al KIO que assegura les actualitzacions automàtiques dels llistats del «nepomuksearch».
 Comment[cs]=Pomocný KIO modul pro zajištění automatických aktualizací seznamů Vyhledávání Nepomuku.
@@ -87,7 +90,7 @@ Comment[ga]=Modúl cabhrach le haghaidh KIO a dhéanann cinnte go bhfuil eolair
 Comment[he]=מודול עזרה עבור KIO המבטיח עידכון אוטומטי של הרשימות של nepomuksearch.
 Comment[hr]=Pomoćni modul za KIO koji osigurava automatsko ažuriranje izlistavanja nepomuk pretraga.
 Comment[hu]=KIO segédmodul a Nepomuk keresőszolgáltatás listái automatikus frissítésének ellenőrzéséhez.
-Comment[ia]=Modulo de adjuta pro KIO per assecurar actualisationes automatic de listas de nepomuksearch.
+Comment[ia]=Modulo de adjuta pro KIO per assecurar actualisationes automatic de listas de nepomuksearch (cerca de Nepomuk).
 Comment[id]=Modul penolong untuk KIO untuk memastikan pemutakhiran otomatis pengurutan nepomuksearch.
 Comment[is]=KIO hjálpareining sem tryggir sjálfvirkar uppfærslur á leitarlistum Nepomuk leitareiningar.
 Comment[it]=Modulo ausiliare per KIO per assicurare gli aggiornamenti automatici delle liste di ricerca di Nepomuk.
@@ -96,6 +99,7 @@ Comment[kk]=nepomuksearch тізімдерін автоматты түрде ж
 Comment[km]=ម៉ឌុល​កម្មវិធី​ជំនួយ​សម្រាប់ KIO ដើម្បី​ប្រាកដ​ថា ធ្វើ​​បច្ចុប្បន្នភាព​ការ​រាយ​របស់ nepomuksearch ដោយ​ស្វ័យ​ប្រវត្តិ ។
 Comment[ko]=nepomuksearch 목록을 자동으로 업데이트하는 KIO 모듈입니다.
 Comment[lt]=Pagalbos modulis, skirtas KIO, užtikrinantis automatinius atnaujinimus nepomuk paieškos sąrašuose.
+Comment[lv]=KIO palīgmodulis, lai nodrošinātu Nepomuk meklēšanas sarakstu automātiskos atjauninājumus.
 Comment[nb]=Hjelpemodul for KIO – for å sikre automatiske oppdateringer av nepomuksearch-resultater.
 Comment[nds]=Hülpmoduul för de KDE-In-/Utgaav för automaatsch Opfrischen vun Nepomuk-Sööklisten
 Comment[nl]=Helper-module voor KIO om automatisch bijwerken van nepomukzoeklijsten.
@@ -115,6 +119,7 @@ Comment[sr@latin]=Pomoćni modul za K‑U/I koji obezbeđuje automatsko ažurira
 Comment[sv]=Hjälpmodul för I/O-slavar för att försäkra att automatisk uppdatering görs av Nepomuk söklistningar.
 Comment[th]=มอดูลช่วยสำหรับ KIO เพื่อให้แน่ใจว่าจะมีการปรับปรุงการทำรายการของ nepomuksearch
 Comment[tr]=KIO'nun, nepomuksearch listelerinin otomatik güncellenmesini sağlaması için yardımcı modül.
+Comment[ug]=KIO نىڭ nepomuk ئىزدەش تىزىمىنى ئۆزلۈكىدىن يېڭىلاشقا ئىشلىتىدىغان ياردەمچى بۆلىكى
 Comment[uk]=Допоміжний модуль KIO для забезпечення автоматичного оновлення списків nepomuksearch.
 Comment[wa]=Module aidant KIO po fé les djivêyes di metaedjes a djoû otomatikes di nepomuksearch
 Comment[x-test]=xxHelper module for KIO to ensure automatic updates of nepomuksearch listings.xx
diff --git a/nepomuk/kioslaves/search/kdedmodule/searchurllistener.cpp b/nepomuk/kioslaves/search/kdedmodule/searchurllistener.cpp
index 62074e6..2333e38 100644
--- a/nepomuk/kioslaves/search/kdedmodule/searchurllistener.cpp
+++ b/nepomuk/kioslaves/search/kdedmodule/searchurllistener.cpp
@@ -27,12 +27,15 @@
 #include <kdebug.h>
 #include <nepomuk/result.h>
 #include <nepomuk/query.h>
+#include <nepomuk/resource.h>
 
 #include <QtCore/QHash>
 #include <QtDBus/QDBusConnection>
 #include <QtDBus/QDBusObjectPath>
 #include <QtDBus/QDBusReply>
 
+#include <Soprano/BindingSet>
+
 
 Nepomuk::SearchUrlListener::SearchUrlListener( const KUrl& queryUrl, const KUrl& notifyUrl )
     : QObject( 0 ),
@@ -92,14 +95,20 @@ void Nepomuk::SearchUrlListener::slotNewEntries( const QList<Nepomuk::Query::Res
 }
 
 
-void Nepomuk::SearchUrlListener::slotEntriesRemoved( const QStringList& entries )
+void Nepomuk::SearchUrlListener::slotEntriesRemoved( const QList<Nepomuk::Query::Result>& entries )
 {
     QStringList urls;
-    foreach( const QString& uri, entries ) {
+    foreach( const Query::Result& result, entries ) {
+        // make sure we use the exact same name used in searchfolder.cpp
+        KUrl url( result.resource().resourceUri() );
+        if( result.requestProperties().contains(Nepomuk::Vocabulary::NIE::url()) )
+            url = result[Nepomuk::Vocabulary::NIE::url()].uri();
+
         KUrl resultUrl( m_notifyUrl );
-        resultUrl.addPath( Nepomuk::resourceUriToUdsName( uri ) );
+        resultUrl.addPath( Nepomuk::resourceUriToUdsName( url ) );
         urls << resultUrl.url();
     }
+    kDebug() << urls;
     org::kde::KDirNotify::emitFilesRemoved( urls );
 }
 
@@ -149,8 +158,8 @@ void Nepomuk::SearchUrlListener::createInterface()
                                                          QDBusConnection::sessionBus() );
         connect( m_queryInterface, SIGNAL( newEntries( QList<Nepomuk::Query::Result> ) ),
                  this, SLOT( slotNewEntries( QList<Nepomuk::Query::Result> ) ) );
-        connect( m_queryInterface, SIGNAL( entriesRemoved( QStringList ) ),
-                 this, SLOT( slotEntriesRemoved( QStringList ) ) );
+        connect( m_queryInterface, SIGNAL( entriesRemoved( QList<Nepomuk::Query::Result> ) ),
+                 this, SLOT( slotEntriesRemoved( QList<Nepomuk::Query::Result> ) ) );
         m_queryInterface->listen();
     }
 }
diff --git a/nepomuk/kioslaves/search/kdedmodule/searchurllistener.h b/nepomuk/kioslaves/search/kdedmodule/searchurllistener.h
index fdf2700..ba2d3bc 100644
--- a/nepomuk/kioslaves/search/kdedmodule/searchurllistener.h
+++ b/nepomuk/kioslaves/search/kdedmodule/searchurllistener.h
@@ -42,9 +42,12 @@ namespace Nepomuk {
         int ref();
         int unref();
 
+        KUrl queryUrl() const { return m_queryUrl; }
+        KUrl notificationUrl() const { return m_notifyUrl; }
+
     private Q_SLOTS:
         void slotNewEntries( const QList<Nepomuk::Query::Result>& entries );
-        void slotEntriesRemoved( const QStringList& entries );
+        void slotEntriesRemoved( const QList<Nepomuk::Query::Result>& entries );
         void slotQueryServiceInitialized( bool success );
 
     private:
diff --git a/nepomuk/kioslaves/search/kio_nepomuksearch.cpp b/nepomuk/kioslaves/search/kio_nepomuksearch.cpp
index 58edd26..e68b8bc 100644
--- a/nepomuk/kioslaves/search/kio_nepomuksearch.cpp
+++ b/nepomuk/kioslaves/search/kio_nepomuksearch.cpp
@@ -300,50 +300,9 @@ bool Nepomuk::SearchProtocol::rewriteUrl( const KUrl& url, KUrl& newURL )
 
 void Nepomuk::SearchProtocol::prepareUDSEntry( KIO::UDSEntry& uds, bool listing ) const
 {
-    // for performace reasons we do encode the result's resource URI in the UDS_NAME
-    // Otherwise we would have to re-query for each stat operation
-    // This is simple for "direct" query results (SearchFolder takes care of that)
-    // but a bit harder for items in results that are folders.
-    // In the latter case we get the parent folder's resource URI (which is encoded in
-    // the UDS_NAME) and append the filename.
-    //
-    // Also note that results listed via a SearchFolder will never go through this method
-    // since they are listed directly and not via a forward. Forwarding will only happen
-    // for search results that are folders and for non-listing operations.
-
-    kDebug() << requestedUrl() << processedUrl() << uds.stringValue(KIO::UDSEntry::UDS_NAME);
-    const QString name = uds.stringValue(KIO::UDSEntry::UDS_NAME);
-    if(name != QLatin1String(".") && name != QLatin1String("..")) {
-        // let the ForwardingSlaveBase create UDS_LOCAL_PATH and mimetype entries
-        // This call depends on the original UDS_NAME which we change below. Thus, it
-        // is important to let ForwardingSlaveBase do its thing before we start ours
-        ForwardingSlaveBase::prepareUDSEntry( uds, listing );
-
-        // encode the URL in the UDS_NAME to prevent a re-query in stat and friends
-        KUrl resourceUrl(processedUrl());
-        if(listing) {
-            resourceUrl.addPath(name);
-        }
-        uds.insert(KIO::UDSEntry::UDS_NAME, Nepomuk::resourceUriToUdsName(resourceUrl));
-        if ( !uds.contains( KIO::UDSEntry::UDS_DISPLAY_NAME ) ) {
-            uds.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, name);
-        }
-
-        // There is a trade-off between using UDS_TARGET_URL or not. The advantage is that we get proper
-        // file names in opening applications and non-KDE apps can handle the URLs properly. The downside
-        // is that we lose the context information, i.e. query results cannot be browsed in the opening
-        // application. We decide pro-filenames and pro-non-kde-apps here.
-        if( resourceUrl.isLocalFile() ) {
-            if ( uds.isDir() ) {
-                Query::FileQuery query;
-                query.addIncludeFolder( resourceUrl );
-                uds.insert( KIO::UDSEntry::UDS_NEPOMUK_QUERY, query.toString() );
-            }
-            else {
-                uds.insert( KIO::UDSEntry::UDS_TARGET_URL, resourceUrl.url() );
-            }
-        }
-    }
+    // do nothing - we do everything in SearchFolder::statResult
+    Q_UNUSED(uds);
+    Q_UNUSED(listing);
 }
 
 
diff --git a/nepomuk/kioslaves/search/queryutils.h b/nepomuk/kioslaves/search/queryutils.h
index 433936e..3b5b707 100644
--- a/nepomuk/kioslaves/search/queryutils.h
+++ b/nepomuk/kioslaves/search/queryutils.h
@@ -22,11 +22,17 @@
 #ifndef QUERYUTILS_H
 #define QUERYUTILS_H
 
+#include "kext.h"
+
 #include <KUrl>
 #include <KDebug>
 
 #include <Nepomuk/Query/Query>
+#include <Nepomuk/Query/OptionalTerm>
+#include <Nepomuk/Query/ComparisonTerm>
+#include <Nepomuk/Query/AndTerm>
 #include <Nepomuk/Vocabulary/NIE>
+#include <Nepomuk/Vocabulary/NFO>
 
 
 namespace Nepomuk {
@@ -40,9 +46,43 @@ namespace Nepomuk {
             query = Nepomuk::Query::Query::fromQueryUrl( url );
 
             // request properties to easily create UDSEntry instances
-            QList<Query::Query::RequestProperty> reqProperties;
+            QList<Query::RequestProperty> reqProperties;
             // local URL
-            reqProperties << Query::Query::RequestProperty( Nepomuk::Vocabulary::NIE::url(), !query.isFileQuery() );
+            reqProperties << Query::RequestProperty( Nepomuk::Vocabulary::NIE::url(), !query.isFileQuery() );
+#ifdef Q_OS_UNIX
+            if( query.isFileQuery() ) {
+                // file size
+                ComparisonTerm contentSizeTerm( Nepomuk::Vocabulary::NIE::contentSize(), Term() );
+                contentSizeTerm.setVariableName( QLatin1String("size") );
+                // mimetype
+                ComparisonTerm mimetypeTerm( Nepomuk::Vocabulary::NIE::mimeType(), Term() );
+                mimetypeTerm.setVariableName( QLatin1String("mime") );
+                // mtime
+                ComparisonTerm mtimeTerm( Nepomuk::Vocabulary::NIE::lastModified(), Term() );
+                mtimeTerm.setVariableName( QLatin1String("mtime") );
+                // mode
+                ComparisonTerm modeTerm( Nepomuk::Vocabulary::KExt::unixFileMode(), Term() );
+                modeTerm.setVariableName( QLatin1String("mode") );
+                // user
+                ComparisonTerm userTerm( Nepomuk::Vocabulary::KExt::unixFileOwner(), Term() );
+                userTerm.setVariableName( QLatin1String("user") );
+                // group
+                ComparisonTerm groupTerm( Nepomuk::Vocabulary::KExt::unixFileGroup(), Term() );
+                groupTerm.setVariableName( QLatin1String("group") );
+
+                // instead of separate request properties we use one optional and term. That way
+                // all or none of the properties will be bound which makes handling the data in
+                // SearchFolder::statResult much simpler.
+                AndTerm filePropertiesTerm;
+                filePropertiesTerm.addSubTerm( contentSizeTerm );
+                filePropertiesTerm.addSubTerm( mimetypeTerm );
+                filePropertiesTerm.addSubTerm( mtimeTerm );
+                filePropertiesTerm.addSubTerm( modeTerm );
+                filePropertiesTerm.addSubTerm( userTerm );
+                filePropertiesTerm.addSubTerm( groupTerm );
+                query = query && OptionalTerm::optionalizeTerm( filePropertiesTerm );
+            }
+#endif // Q_OS_UNIX
             query.setRequestProperties( reqProperties );
 
             if ( query.isValid() ) {
diff --git a/nepomuk/kioslaves/search/searchfolder.cpp b/nepomuk/kioslaves/search/searchfolder.cpp
index 6fc2f85..aeebc3f 100644
--- a/nepomuk/kioslaves/search/searchfolder.cpp
+++ b/nepomuk/kioslaves/search/searchfolder.cpp
@@ -192,11 +192,6 @@ void Nepomuk::SearchFolder::statResults()
 namespace {
     bool statFile( const KUrl& url, const KUrl& fileUrl, KIO::UDSEntry& uds )
     {
-        // the akonadi kio slave is just way too slow and
-        // in KDE 4.4 akonadi items should have nepomuk:/res/<uuid> URIs anyway
-        if ( url.scheme() == QLatin1String( "akonadi" ) )
-            return false;
-
         if ( !fileUrl.isEmpty() ) {
             if ( KIO::StatJob* job = KIO::stat( fileUrl, KIO::HideProgressInfo ) ) {
                 // we do not want to wait for the event loop to delete the job
@@ -227,74 +222,121 @@ namespace {
 KIO::UDSEntry Nepomuk::SearchFolder::statResult( const Query::Result& result )
 {
     Resource res( result.resource() );
-    KUrl url( res.resourceUri() );
+    const KUrl uri( res.resourceUri() );
     KUrl nieUrl( result[Nepomuk::Vocabulary::NIE::url()].uri() );
-    if ( nieUrl.isEmpty() )
-        nieUrl = Nepomuk::nepomukToFileUrl( url );
 
+    // the additional bindings that we only have on unix systems
+    // Either all are bound or none of them.
+    // see also parseQueryUrl (queryutils.h)
+    const Soprano::BindingSet additionalVars = result.additionalBindings();
+
+    // the UDSEntry that will contain the final result to list
     KIO::UDSEntry uds;
-    if ( statFile( url, nieUrl, uds ) ) {
-        if ( !nieUrl.isEmpty() ) {
-            if ( nieUrl.isLocalFile() ) {
-                // There is a trade-off between using UDS_TARGET_URL or not. The advantage is that we get proper
-                // file names in opening applications and non-KDE apps can handle the URLs properly. The downside
-                // is that we lose the context information, i.e. query results cannot be browsed in the opening
-                // application. We decide pro-filenames and pro-non-kde-apps here.
-                if( !uds.isDir() )
-                    uds.insert( KIO::UDSEntry::UDS_TARGET_URL, nieUrl.url() );
-                uds.insert( KIO::UDSEntry::UDS_LOCAL_PATH, nieUrl.toLocalFile() );
-            }
 
+#ifdef Q_OS_UNIX
+    if( !nieUrl.isEmpty() &&
+            nieUrl.isLocalFile() &&
+            additionalVars[QLatin1String("mtime")].isLiteral() ) {
+        // make sure we have unique names for everything
+        uds.insert( KIO::UDSEntry::UDS_NAME, resourceUriToUdsName( nieUrl ) );
+
+        // set the name the user will see
+        uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, nieUrl.fileName() );
+
+        // set the basic file information which we got from Nepomuk
+        uds.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, additionalVars[QLatin1String("mtime")].literal().toDateTime().toTime_t() );
+        uds.insert( KIO::UDSEntry::UDS_SIZE, additionalVars[QLatin1String("size")].literal().toInt() );
+        uds.insert( KIO::UDSEntry::UDS_FILE_TYPE, additionalVars[QLatin1String("mode")].literal().toInt() & S_IFMT );
+        uds.insert( KIO::UDSEntry::UDS_ACCESS, additionalVars[QLatin1String("mode")].literal().toInt() & 07777 );
+        uds.insert( KIO::UDSEntry::UDS_USER, additionalVars[QLatin1String("user")].toString() );
+        uds.insert( KIO::UDSEntry::UDS_GROUP, additionalVars[QLatin1String("group")].toString() );
+
+        // since we change the UDS_NAME KFileItem cannot handle mimetype and such anymore
+        uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, additionalVars[QLatin1String("mime")].toString() );
+        if( uds.stringValue(KIO::UDSEntry::UDS_MIME_TYPE).isEmpty())
+            uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, KMimeType::findByUrl(nieUrl)->name() );
+    }
+    else
+#endif // Q_OS_UNIX
+    {
+        // not a simple local file result
+
+        // check if we have a pimo thing relating to a file
+        if ( nieUrl.isEmpty() )
+            nieUrl = Nepomuk::nepomukToFileUrl( uri );
+
+        // try to stat the file
+        if ( statFile( uri, nieUrl, uds ) ) {
             // make sure we have unique names for everything
-            uds.insert( KIO::UDSEntry::UDS_NAME, resourceUriToUdsName( nieUrl ) );
-        }
-        else {
-            // make sure we have unique names for everything
-            uds.insert( KIO::UDSEntry::UDS_NAME, resourceUriToUdsName( url ) );
-        }
+            // We encode the resource URL or URI into the name so subsequent calls to stat or
+            // other non-listing commands can easily forward to the appropriate slave.
+            if ( !nieUrl.isEmpty() ) {
+                uds.insert( KIO::UDSEntry::UDS_NAME, resourceUriToUdsName( nieUrl ) );
+            }
+            else {
+                uds.insert( KIO::UDSEntry::UDS_NAME, resourceUriToUdsName( uri ) );
+            }
 
-        // needed since the file:/ KIO slave does not create them and KFileItem::nepomukUri()
-        // cannot know that it is a local file since it is forwarded
-        uds.insert( KIO::UDSEntry::UDS_NEPOMUK_URI, url.url() );
-
-        // make sure we do not use these ugly names for display
-        if ( !uds.contains( KIO::UDSEntry::UDS_DISPLAY_NAME ) ) {
-            // by checking nieUrl we avoid loading the resource for local files
-            if ( nieUrl.isEmpty() &&
-                 res.hasType( Nepomuk::Vocabulary::PIMO::Thing() ) ) {
-                if ( !res.pimoThing().groundingOccurrences().isEmpty() ) {
-                    res = res.pimoThing().groundingOccurrences().first();
+            // make sure we do not use these ugly names for display
+            if ( !uds.contains( KIO::UDSEntry::UDS_DISPLAY_NAME ) ) {
+                if ( nieUrl.isEmpty() &&
+                        res.hasType( Nepomuk::Vocabulary::PIMO::Thing() ) ) {
+                    if ( !res.pimoThing().groundingOccurrences().isEmpty() ) {
+                        res = res.pimoThing().groundingOccurrences().first();
+                        nieUrl = res.property(Nepomuk::Vocabulary::NIE::url()).toUrl();
+                    }
                 }
-            }
 
-            if ( !nieUrl.isEmpty() ) {
-                uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, nieUrl.fileName() );
+                if ( !nieUrl.isEmpty() ) {
+                    uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, nieUrl.fileName() );
 
-                // since we change the UDS_NAME KFileItem cannot handle mimetype and such anymore
-                QString mimetype = uds.stringValue( KIO::UDSEntry::UDS_MIME_TYPE );
-                if ( mimetype.isEmpty() ) {
-                    mimetype = KMimeType::findByUrl(nieUrl)->name();
-                    uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, mimetype );
+                    // since we change the UDS_NAME KFileItem cannot handle mimetype and such anymore
+                    QString mimetype = uds.stringValue( KIO::UDSEntry::UDS_MIME_TYPE );
+                    if ( mimetype.isEmpty() ) {
+                        mimetype = KMimeType::findByUrl(nieUrl)->name();
+                        uds.insert( KIO::UDSEntry::UDS_MIME_TYPE, mimetype );
+                    }
+                }
+                else {
+                    uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, res.genericLabel() );
                 }
-            }
-            else {
-                uds.insert( KIO::UDSEntry::UDS_DISPLAY_NAME, res.genericLabel() );
             }
         }
+        else {
+            kDebug() << "Stating" << result.resource().resourceUri() << "failed";
+            return KIO::UDSEntry();
+        }
+    }
 
-        QString excerpt = result.excerpt();
-        if( !excerpt.isEmpty() ) {
-            // KFileItemDelegate cannot handle rich text yet. Thus we need to remove the formatting.
-            QTextDocument doc;
-            doc.setHtml(excerpt);
-            excerpt = doc.toPlainText();
-            uds.insert( KIO::UDSEntry::UDS_COMMENT, i18n("Search excerpt: %1", excerpt) );
+    if( !nieUrl.isEmpty() ) {
+        // There is a trade-off between using UDS_URL or not. The advantage is that we get proper
+        // file names in opening applications and non-KDE apps can handle the URLs properly. The downside
+        // is that we lose the context information, i.e. query results cannot be browsed in the opening
+        // application. We decide pro-filenames and pro-non-kde-apps here.
+        if( !uds.isDir() ) {
+            uds.insert( KIO::UDSEntry::UDS_TARGET_URL, nieUrl.url() );
         }
 
-        return uds;
+        // set the local path so that KIO can handle the rest
+        if( nieUrl.isLocalFile() )
+            uds.insert( KIO::UDSEntry::UDS_LOCAL_PATH, nieUrl.toLocalFile() );
     }
     else {
-        kDebug() << "Stating" << result.resource().resourceUri() << "failed";
-        return KIO::UDSEntry();
+        uds.insert( KIO::UDSEntry::UDS_TARGET_URL, uri.url() );
     }
+
+    // Tell KIO which Nepomuk resource this actually is
+    uds.insert( KIO::UDSEntry::UDS_NEPOMUK_URI, uri.url() );
+
+    // add optional full-text search excerpts
+    QString excerpt = result.excerpt();
+    if( !excerpt.isEmpty() ) {
+        // KFileItemDelegate cannot handle rich text yet. Thus we need to remove the formatting.
+        QTextDocument doc;
+        doc.setHtml(excerpt);
+        excerpt = doc.toPlainText();
+        uds.insert( KIO::UDSEntry::UDS_COMMENT, i18n("Search excerpt: %1", excerpt) );
+    }
+
+    return uds;
 }
diff --git a/nepomuk/ontologies/CMakeLists.txt b/nepomuk/ontologies/CMakeLists.txt
index c5fed3f..7a7a590 100644
--- a/nepomuk/ontologies/CMakeLists.txt
+++ b/nepomuk/ontologies/CMakeLists.txt
@@ -1,8 +1,14 @@
 project(nepomuk_ontologies)
 
-set(ONTO_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/ontology/kde)
 configure_file(kuvo.ontology.in ${CMAKE_CURRENT_BINARY_DIR}/kuvo.ontology)
-install(FILES kuvo.trig ${CMAKE_CURRENT_BINARY_DIR}/kuvo.ontology DESTINATION ${ONTO_INSTALL_DIR})
-
 configure_file(nrio.ontology.in ${CMAKE_CURRENT_BINARY_DIR}/nrio.ontology)
-install(FILES nrio.trig ${CMAKE_CURRENT_BINARY_DIR}/nrio.ontology DESTINATION ${ONTO_INSTALL_DIR})
+configure_file(kext.ontology.in ${CMAKE_CURRENT_BINARY_DIR}/kext.ontology)
+
+install(FILES
+  kuvo.trig
+  ${CMAKE_CURRENT_BINARY_DIR}/kuvo.ontology
+  nrio.trig
+  ${CMAKE_CURRENT_BINARY_DIR}/nrio.ontology
+  kext.trig
+  ${CMAKE_CURRENT_BINARY_DIR}/kext.ontology 
+DESTINATION ${CMAKE_INSTALL_PREFIX}/share/ontology/kde)
diff --git a/nepomuk/ontologies/kext.ontology.in b/nepomuk/ontologies/kext.ontology.in
new file mode 100644
index 0000000..a9ada44
--- /dev/null
+++ b/nepomuk/ontologies/kext.ontology.in
@@ -0,0 +1,8 @@
+[Ontology]
+Version=1.0
+Name=KDE Extensions Ontology
+Comment=The KDE Extensions Ontology contains extensions to the shared-desktop-ontologies that are not generic enough.
+Namespace=http://nepomuk.kde.org/ontologies/2010/11/29/kext#
+Path=${CMAKE_INSTALL_PREFIX}/share/ontology/kde/kext.trig
+MimeType=application/x-trig
+Type=Data
diff --git a/nepomuk/ontologies/kext.trig b/nepomuk/ontologies/kext.trig
new file mode 100644
index 0000000..be5d041
--- /dev/null
+++ b/nepomuk/ontologies/kext.trig
@@ -0,0 +1,116 @@
+#
+# Copyright (c) 2010-2011 Sebastian Trueg <trueg@kde.org>
+# All rights reserved, licensed under either CC-BY or BSD.
+#
+# You are free:
+#  * to Share - to copy, distribute and transmit the work
+#  * to Remix - to adapt the work
+# Under the following conditions:
+#  * Attribution - You must attribute the work in the manner specified by the author
+#    or licensor (but not in any way that suggests that they endorse you or your use 
+#    of the work).
+#
+# Redistribution and use in source and binary forms, with or without modification, 
+# are permitted provided that the following conditions are met:
+#  * Redistributions of source code must retain the above copyright notice, this 
+#    list of conditions and the following disclaimer.
+#  * Redistributions in binary form must reproduce the above copyright notice, this 
+#    list of conditions and the following disclaimer in the documentation and/or 
+#    other materials provided with the distribution.
+#  * Neither the names of the authors nor the names of contributors may
+#    be used to endorse or promote products derived from this ontology without 
+#    specific prior written permission.
+#
+# THIS ONTOLOGY IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS ONTOLOGY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix nao:     <http://www.semanticdesktop.org/ontologies/2007/08/15/nao#> .
+@prefix nrl:     <http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#> .
+@prefix nie:     <http://www.semanticdesktop.org/ontologies/2007/01/19/nie#> .
+@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
+@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix kext:    <http://nepomuk.kde.org/ontologies/2010/11/29/kext#> .
+@prefix nfo:     <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#> .
+
+kext: {
+    kext:unixFileMode
+          a      rdf:Property ;
+          rdfs:label "Unix file mode" ;
+          rdfs:comment "The file mode value as seen on unix file systems." ;
+          rdfs:domain nfo:FileDataObject ;
+          rdfs:range xsd:integer ;
+          nrl:maxCardinality 1 ;
+          nao:userVisible false .
+
+    kext:unixFileOwner
+          a      rdf:Property ;
+          rdfs:label "Unix file owner" ;
+          rdfs:comment "The owner of the file as seen on unix file systems. This is intended as the low-level counterpart to nfo:owner." ;
+          rdfs:domain nfo:FileDataObject ;
+          rdfs:range xsd:string ;
+          nrl:maxCardinality 1 ;
+          nao:userVisible false .
+
+    kext:unixFileGroup
+          a      rdf:Property ;
+          rdfs:label "Unix file group" ;
+          rdfs:comment "The group of the file as seen on unix file systems." ;
+          rdfs:domain nfo:FileDataObject ;
+          rdfs:range xsd:string ;
+          nrl:maxCardinality 1 ;
+          nao:userVisible false .
+
+    kext:Activity
+          a rdfs:Class ;
+          rdfs:subClassOf rdfs:Resource ;
+          rdfs:label "activity" ;
+          rdfs:comment "A Plasma activity." .
+
+    kext:usedActivity
+          a rdf:Property ;
+          rdfs:label "used activity" ;
+          rdfs:comment "The activity that was active when resource was created. This is mostly used for graphs or desktop events." ;
+          rdfs:domain rdfs:Resource ;
+          rdfs:range kext:Activity ;
+          nrl:maxCardinality 1 ;
+          nao:userVisible false .
+
+    kext:activityIdentifier
+          a rdf:Property ;
+          rdfs:subPropertyOf nao:identifier ;
+          rdfs:label "activity identifier" ;
+          rdfs:comment "The unique ID of the activity as used outside of Nepomuk. Typically this is a UUID." ;
+          rdfs:domain kext:Activity ;
+          rdfs:range xsd:string ;
+          nrl:cardinality 1 ;
+          nao:userVisible false .
+}
+
+<http://nepomuk.kde.org/ontologies/2010/11/29/kext/metadata> {
+    <http://nepomuk.kde.org/ontologies/2010/11/29/kext/metadata>
+          a       nrl:GraphMetadata ;
+          nrl:coreGraphMetadataFor kext: .
+
+
+    kext:
+          a       nrl:Ontology , nrl:DocumentGraph ;
+          nao:prefLabel "KDE Extensions Ontology" ;
+          nao:hasDefaultNamespace "http://nepomuk.kde.org/ontologies/2010/11/29/kext#" ;
+          nao:hasDefaultNamespaceAbbreviation "kext" ;
+          nao:lastModified "2011-06-15T18:09:43Z" ;
+          nao:serializationLanguage "TriG" ;
+          nao:status "Unstable" ;
+          nrl:updatable "0" ;
+          nao:version "2" .
+}
+
diff --git a/nepomuk/ontologies/kuvo.trig b/nepomuk/ontologies/kuvo.trig
index 38e0735..11ac9f4 100644
--- a/nepomuk/ontologies/kuvo.trig
+++ b/nepomuk/ontologies/kuvo.trig
@@ -41,6 +41,7 @@
 @prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 @prefix kuvo:    <http://nepomuk.kde.org/ontologies/2010/08/18/kuvo#> .
 @prefix nuao:    <http://www.semanticdesktop.org/ontologies/2010/01/25/nuao#> .
+@prefix nfo:     <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#> .
 
 kuvo: {
     # Setting a baseline calms down the algorithm that creates all the entries.
@@ -50,6 +51,9 @@ kuvo: {
     nao:userVisible
         nao:userVisible false .
 
+    nao:hasSubResource
+        nao:userVisible false .
+
     nuao:Event
         nao:userVisible false .
 
@@ -77,12 +81,13 @@ kuvo: {
 
     kuvo:
           a       nrl:Ontology , nrl:DocumentGraph ;
+          nao:prefLabel "KDE User Visibility Ontology" ;
           nao:hasDefaultNamespace "http://nepomuk.kde.org/ontologies/2010/08/18/kuvo#" ;
           nao:hasDefaultNamespaceAbbreviation "kuvo" ;
-          nao:lastModified "2010-08-18T12:31:43Z" ;
+          nao:lastModified "2010-11-29T12:31:43Z" ;
           nao:serializationLanguage "TriG" ;
           nao:status "Unstable" ;
           nrl:updatable "0" ;
-          nao:version "1" .
+          nao:version "2" .
 }
 
diff --git a/nepomuk/removed-services/CMakeLists.txt b/nepomuk/removed-services/CMakeLists.txt
index fba5e77..3ff8863 100644
--- a/nepomuk/removed-services/CMakeLists.txt
+++ b/nepomuk/removed-services/CMakeLists.txt
@@ -4,5 +4,6 @@ install(
   FILES
   nepomukactivitiesservice.desktop
   nepomukontologyloader.desktop
+  nepomukremovablestorageservice.desktop
   DESTINATION ${SERVICES_INSTALL_DIR})
 
diff --git a/nepomuk/removed-services/nepomukremovablestorageservice.desktop b/nepomuk/removed-services/nepomukremovablestorageservice.desktop
new file mode 100644
index 0000000..e1e3e17
--- /dev/null
+++ b/nepomuk/removed-services/nepomukremovablestorageservice.desktop
@@ -0,0 +1,2 @@
+[Desktop Entry]
+Hidden=true
diff --git a/nepomuk/server/CMakeLists.txt b/nepomuk/server/CMakeLists.txt
index c468c0e..e0a23ad 100644
--- a/nepomuk/server/CMakeLists.txt
+++ b/nepomuk/server/CMakeLists.txt
@@ -38,13 +38,6 @@ qt4_add_dbus_interface(nepomukserver_SRCS
 
 kde4_add_kdeinit_executable(nepomukserver ${nepomukserver_SRCS})
 
-if (Q_WS_MAC)
-    set_target_properties(nepomukserver PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/Info.plist.template)
-    set_target_properties(nepomukserver PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.nepomukserver")
-    set_target_properties(nepomukserver PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Nepomuk Server")
-endif (Q_WS_MAC)
-
-
 target_link_libraries(kdeinit_nepomukserver
   ${KDE4_KDEUI_LIBS}
   ${SOPRANO_LIBRARIES}
diff --git a/nepomuk/server/nepomukserver.desktop b/nepomuk/server/nepomukserver.desktop
index 5f8ffc2..4e69092 100644
--- a/nepomuk/server/nepomukserver.desktop
+++ b/nepomuk/server/nepomukserver.desktop
@@ -12,6 +12,7 @@ Name[be@latin]=Server „Nepomuk”
 Name[bg]=Сървър Nepomuk
 Name[bn]=নেপোমুক সার্ভার
 Name[bn_IN]=Nepomuk সার্ভার
+Name[bs]=Server Nepomuka
 Name[ca]=Servidor Nepomuk
 Name[ca@valencia]=Servidor Nepomuk
 Name[cs]=Server Nepomuku
@@ -77,6 +78,7 @@ Name[te]=Nepomuk సేవిక
 Name[tg]=Хидматгоҳи Nepomuk
 Name[th]=บริการ Nepomuk
 Name[tr]=Nepomuk Sunucu
+Name[ug]=Nepomuk مۇلازىمېتىرى
 Name[uk]=Сервер Nepomuk
 Name[uz]=Nepomuk serveri
 Name[uz@cyrillic]=Nepomuk сервери
@@ -89,6 +91,7 @@ Comment[ar]=يوفر خادم نبومك خدمات تخزين ويتحكم بس
 Comment[ast]=Sirvidor de Nepomuk qu'ufre servicios d'atroxamientu y control strigi
 Comment[be@latin]=Server „Nepomuk”, jaki absłuhoŭvaje schovišča źviestak i kiruje słužbaj „Strigi”
 Comment[bg]=Сървърът Nepomuk предлага контрол на strigi и съхранение
+Comment[bs]=Server Nepomuka pruža servis za skladištenje i kontrolu Strigija
 Comment[ca]=El servidor Nepomuk proporciona serveis d'emmagatzematge i de control de l'Strigi
 Comment[ca@valencia]=El servidor Nepomuk proporciona serveis d'emmagatzematge i de control de l'Strigi
 Comment[cs]=Server Nepomuk poskytuje služby pro ukládání a pro ovládání Strigi
@@ -153,6 +156,7 @@ Comment[te]=Nepomuk సేవిక నిల్వ సేవలను మరి
 Comment[tg]=Хидматгоҳи Nepomuk хидматҳои захира ва идоракунии маълумот дастрас мекунад
 Comment[th]=บริการ Nepomuk จะให้บริการจัดเก็บข้อมูลและทำการควบคุม strigi
 Comment[tr]=Nepomuk Sunucusu Depolama servislerini ve Strigi uygulamasının kontrolünü sağlar
+Comment[ug]=Nepomuk مۇلازىمېتىر ساقلاش مۇلازىمىتى ۋە strigi تىزگىنلەشنى تەمىنلىدى
 Comment[uk]=Сервер Nepomuk надає служби збереження і керування strigi
 Comment[wa]=Li sierveu Nepomuk ki dene des siervices di stocaedje et des contrôles po strigi
 Comment[x-test]=xxThe Nepomuk Server providing Storage services and strigi controllingxx
diff --git a/nepomuk/server/nepomukservice.desktop b/nepomuk/server/nepomukservice.desktop
index 9daad12..9d443e8 100644
--- a/nepomuk/server/nepomukservice.desktop
+++ b/nepomuk/server/nepomukservice.desktop
@@ -9,6 +9,7 @@ Comment[be@latin]=Słužba „Nepomuk”
 Comment[bg]=Услуга Nepomuk
 Comment[bn]=নেপোমুক সার্ভিস
 Comment[bn_IN]=Nepomuk পরিসেবা
+Comment[bs]=Servis Nepomuka
 Comment[ca]=Servei Nepomuk
 Comment[ca@valencia]=Servei Nepomuk
 Comment[cs]=Nepomuk služba
@@ -73,6 +74,7 @@ Comment[te]=Nepomuk సేవ
 Comment[tg]=Хидматҳои Nepomuk
 Comment[th]=บริการ Neomuk
 Comment[tr]=Nepomuk Servisi
+Comment[ug]=Nepomuk مۇلازىمىتى
 Comment[uk]=Служба Nepomuk
 Comment[uz]=Nepomuk xizmati
 Comment[uz@cyrillic]=Nepomuk хизмати
diff --git a/nepomuk/server/servicecontroller.cpp b/nepomuk/server/servicecontroller.cpp
index e903782..13d83cc 100644
--- a/nepomuk/server/servicecontroller.cpp
+++ b/nepomuk/server/servicecontroller.cpp
@@ -305,6 +305,9 @@ void Nepomuk::ServiceController::slotServiceUnregistered( const QString& service
     // on its restart-on-crash feature and have to do it manually. Afterwards it is back
     // to normal
     if( serviceName == dbusServiceName( name() ) ) {
+
+        emit serviceStopped( this );
+
         if( d->attached && d->started ) {
             kDebug() << "Attached service" << name() << "went down. Restarting ourselves.";
             start();
diff --git a/nepomuk/server/servicecontroller.h b/nepomuk/server/servicecontroller.h
index c48714b..75a6f73 100644
--- a/nepomuk/server/servicecontroller.h
+++ b/nepomuk/server/servicecontroller.h
@@ -78,6 +78,12 @@ namespace Nepomuk {
          */
         void serviceInitialized( ServiceController* );
         
+        /**
+         * Emitted once the service has stopped, i.e.
+         * once its DBus service is gone.
+         */
+        void serviceStopped( ServiceController* );
+
     private Q_SLOTS:
         void slotProcessFinished( bool );
         void slotServiceRegistered( const QString& serviceName );
diff --git a/nepomuk/server/servicemanager.cpp b/nepomuk/server/servicemanager.cpp
index a5becd7..475c5d6 100644
--- a/nepomuk/server/servicemanager.cpp
+++ b/nepomuk/server/servicemanager.cpp
@@ -170,6 +170,12 @@ public:
      */
     void _k_serviceInitialized( ServiceController* );
 
+    /**
+     * Slot connected to all ServiceController::serviceStopped
+     * signals.
+     */
+    void _k_serviceStopped( ServiceController* );
+
 private:
     bool m_initialized;
     ServiceManager* q;
@@ -198,6 +204,8 @@ void Nepomuk::ServiceManager::Private::buildServiceMap()
                 ServiceController* sc = new ServiceController( service, q );
                 connect( sc, SIGNAL(serviceInitialized(ServiceController*)),
                          q, SLOT(_k_serviceInitialized(ServiceController*)) );
+                connect( sc, SIGNAL(serviceStopped(ServiceController*)),
+                         q, SLOT(_k_serviceStopped(ServiceController*)) );
                 services.insert( sc->name(), sc );
             }
         }
@@ -226,7 +234,7 @@ void Nepomuk::ServiceManager::Private::startService( ServiceController* sc )
         bool needToQueue = false;
         foreach( const QString &dependency, dependencyTree[sc->name()] ) {
             ServiceController* depSc = findService( dependency );
-            if ( !depSc->isInitialized() ) {
+            if ( !needToQueue && !depSc->isInitialized() ) {
                 kDebug() << "Queueing" << sc->name() << "due to dependency" << dependency;
                 pendingServices.insert( sc );
                 needToQueue = true;
@@ -247,16 +255,17 @@ void Nepomuk::ServiceManager::Private::startService( ServiceController* sc )
 
 bool Nepomuk::ServiceManager::Private::stopService( ServiceController* sc )
 {
-    // make sure the service is not scheduled to be started later anymore
-    pendingServices.remove( sc );
+    // shut down any service depending of this one first
+    foreach( const QString &dep, dependencyTree.servicesDependingOn( sc->name() ) ) {
+        ServiceController* sc = services[dep];
+        if( sc->isRunning() ) {
+            pendingServices.insert( sc );
+            stopService( sc );
+        }
+    }
 
     // stop it if already running
     if( sc->isRunning() ) {
-        // shut down any service depending of this one first
-        foreach( const QString &dep, dependencyTree.servicesDependingOn( sc->name() ) ) {
-            stopService( services[dep] );
-        }
-
         sc->stop();
         return true;
     }
@@ -268,6 +277,8 @@ bool Nepomuk::ServiceManager::Private::stopService( ServiceController* sc )
 
 void Nepomuk::ServiceManager::Private::startPendingServices( ServiceController* newService )
 {
+    kDebug() << newService->name() << pendingServices;
+
     // check the list of pending services and start as many as possible
     // (we can start services whose dependencies are initialized)
     QList<ServiceController*> sl = pendingServices.toList();
@@ -284,13 +295,28 @@ void Nepomuk::ServiceManager::Private::startPendingServices( ServiceController*
 void Nepomuk::ServiceManager::Private::_k_serviceInitialized( ServiceController* sc )
 {
     kDebug() << "Service initialized:" << sc->name();
-    if ( !pendingServices.isEmpty() ) {
-        startPendingServices( sc );
-    }
+
+    startPendingServices( sc );
     emit q->serviceInitialized( sc->name() );
 }
 
 
+void Nepomuk::ServiceManager::Private::_k_serviceStopped( ServiceController* sc )
+{
+    kDebug() << "Service stopped:" << sc->name();
+
+    // stop and queue all services depending on the stopped one
+    // this will re-trigger this method until all reverse-deps are stopped
+    foreach( const QString &dep, dependencyTree.servicesDependingOn( sc->name() ) ) {
+        ServiceController* depsc = services[dep];
+        if( depsc->isRunning() ) {
+            kDebug() << "Stopping and queuing rev-dep" << depsc->name();
+            depsc->stop();
+            pendingServices.insert( depsc );
+        }
+    }
+}
+
 
 Nepomuk::ServiceManager::ServiceManager( QObject* parent )
     : QObject(parent),
@@ -350,6 +376,9 @@ bool Nepomuk::ServiceManager::startService( const QString& name )
 bool Nepomuk::ServiceManager::stopService( const QString& name )
 {
     if( ServiceController* sc = d->findService( name ) ) {
+        // make sure the service is not scheduled to be started later anymore
+        d->pendingServices.remove( sc );
+
         return d->stopService( sc );
     }
     return false;
diff --git a/nepomuk/server/servicemanager.h b/nepomuk/server/servicemanager.h
index 069b57c..b6f359b 100644
--- a/nepomuk/server/servicemanager.h
+++ b/nepomuk/server/servicemanager.h
@@ -124,6 +124,7 @@ namespace Nepomuk {
         Private* const d;
 
         Q_PRIVATE_SLOT( d, void _k_serviceInitialized(ServiceController*) )
+        Q_PRIVATE_SLOT( d, void _k_serviceStopped(ServiceController*) )
     };
 }
 
diff --git a/nepomuk/services/CMakeLists.txt b/nepomuk/services/CMakeLists.txt
index 57d8754..19bf3f8 100644
--- a/nepomuk/services/CMakeLists.txt
+++ b/nepomuk/services/CMakeLists.txt
@@ -2,5 +2,4 @@ add_subdirectory(storage)
 add_subdirectory(filewatch)
 add_subdirectory(queryservice)
 add_subdirectory(strigi)
-add_subdirectory(removablestorage)
 add_subdirectory(backupsync)
diff --git a/nepomuk/services/backupsync/gui/CMakeLists.txt b/nepomuk/services/backupsync/gui/CMakeLists.txt
index d95ab8f..f746df0 100644
--- a/nepomuk/services/backupsync/gui/CMakeLists.txt
+++ b/nepomuk/services/backupsync/gui/CMakeLists.txt
@@ -8,9 +8,10 @@ include_directories(
   ${NEPOMUK_INCLUDE_DIR}
   ${libnepomuksync_SOURCE_DIR}
   ${CMAKE_CURRENT_BUILD_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/../service/
   )
 
-set( NepomukBackup_GUI_SRCS 
+set( NepomukBackup_GUI_SRCS
   backupwizardpages.cpp
   backupwizard.cpp
   mergeconflictdelegate.cpp
@@ -19,15 +20,25 @@ set( NepomukBackup_GUI_SRCS
   identifiermodeltree.cpp
   identifierwidget.cpp
   filesystemtree.cpp
-  ../service/dbusoperators.cpp
+
+  # Restoration code
+  identifier.cpp
+  merger.cpp
+  changelogmerger.cpp
+  syncfileidentifier.cpp
+  resourcelog.cpp
+
+   # Code required by restoration
+  ../service/tools.cpp
+  ../service/changelog.cpp
+  ../service/changelogrecord.cpp
+  ../service/identificationset.cpp
+  ../service/syncfile.cpp
 )
 
 #----- DBus interfaces --------
 
 set_source_files_properties(
-  ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.identifier.xml
-  PROPERTIES INCLUDE "../service/dbusoperators.h")
-set_source_files_properties(
   ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.backupmanager.xml
   PROPERTIES INCLUDE "QtCore/QList")
 set_source_files_properties(
@@ -35,12 +46,6 @@ set_source_files_properties(
   PROPERTIES INCLUDE "QtCore/QString")
 
 qt4_add_dbus_interface( NepomukBackup_GUI_SRCS
-                        ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.identifier.xml
-                        identifierinterface )
-qt4_add_dbus_interface( NepomukBackup_GUI_SRCS
-                        ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.merger.xml
-                        mergerinterface )
-qt4_add_dbus_interface( NepomukBackup_GUI_SRCS
                         ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.syncmanager.xml
                         syncmanagerinterface )
 qt4_add_dbus_interface( NepomukBackup_GUI_SRCS
@@ -59,6 +64,13 @@ kde4_add_ui_files( NepomukBackup_GUI_SRCS
   errorpage.ui
 )
 
+#--------- Ontologies -------#
+soprano_add_ontology(NepomukBackup_GUI_SRCS
+   ${CMAKE_CURRENT_SOURCE_DIR}/../../../ontologies/nrio.trig
+  "NRIO"
+  "Nepomuk::Vocabulary"
+  "trig")
+
 kde4_add_executable(nepomukbackup ${NepomukBackup_GUI_SRCS})
 
 target_link_libraries(nepomukbackup
@@ -66,6 +78,7 @@ target_link_libraries(nepomukbackup
   ${KDE4_KIO_LIBS}
   ${NEPOMUK_LIBRARIES}
   ${SOPRANO_LIBRARIES}
+  nepomuksync
   )
 
 install(TARGETS nepomukbackup ${INSTALL_TARGETS_DEFAULT_ARGS} )
diff --git a/nepomuk/services/backupsync/gui/backupwizard.cpp b/nepomuk/services/backupsync/gui/backupwizard.cpp
index c5600ed..103495d 100644
--- a/nepomuk/services/backupsync/gui/backupwizard.cpp
+++ b/nepomuk/services/backupsync/gui/backupwizard.cpp
@@ -45,6 +45,18 @@ Nepomuk::BackupWizard::BackupWizard(QWidget* parent, Qt::WindowFlags flags)
     setPage( Id_RestoreFinalPage, new RestoreFinalPage( this ) );
     setPage( Id_ErrorPage, new ErrorPage( this ) );
     setStartId( Id_IntroPage );
+
+    m_identifier = Identifier::instance();
+    m_merger = Merger::instance();
+
+    // IMPORTANT : We've used "Nepomuk::ChangeLog" in the string cause in the slots, signals, and
+    // connect statement we're using Nepomuk::ChangeLog, NOT ChangeLog
+    qRegisterMetaType<Nepomuk::ChangeLog>("Nepomuk::ChangeLog");
+
+    //registerMetaTypes();
+    connect( m_identifier, SIGNAL( processed( Nepomuk::ChangeLog ) ),
+             m_merger, SLOT( process( Nepomuk::ChangeLog ) ) );
+
 }
 
 void Nepomuk::BackupWizard::startBackup()
diff --git a/nepomuk/services/backupsync/gui/backupwizard.h b/nepomuk/services/backupsync/gui/backupwizard.h
index ab81526..429dc40 100644
--- a/nepomuk/services/backupsync/gui/backupwizard.h
+++ b/nepomuk/services/backupsync/gui/backupwizard.h
@@ -26,6 +26,9 @@
 
 #include <QtGui/QWizard>
 
+#include "identifier.h"
+#include "merger.h"
+
 namespace Nepomuk {
 
     class BackupWizard : public QWizard
@@ -45,10 +48,14 @@ namespace Nepomuk {
             Id_RestoreFinalPage,
             Id_ErrorPage
         };
-        
+
         void startBackup();
         void startRestore();
         void showError(const QString& error);
+
+    public:
+        Identifier* m_identifier;
+        Merger* m_merger;
     };
 
 }
diff --git a/nepomuk/services/backupsync/gui/backupwizardpages.cpp b/nepomuk/services/backupsync/gui/backupwizardpages.cpp
index ad7581d..f94de60 100644
--- a/nepomuk/services/backupsync/gui/backupwizardpages.cpp
+++ b/nepomuk/services/backupsync/gui/backupwizardpages.cpp
@@ -25,7 +25,6 @@
 #include "backupwizard.h"
 
 #include "identifierwidget.h"
-#include "identifierinterface.h"
 
 #include <KDebug>
 #include <KLineEdit>
@@ -51,7 +50,7 @@ int Nepomuk::IntroPage::nextId() const
         return BackupWizard::Id_BackupSettingsPage;
     else if( m_restore->isChecked() )
         return BackupWizard::Id_RestoreSelectionPage;
-        
+
     return -1;
 }
 
@@ -185,9 +184,7 @@ Nepomuk::RestorePage::RestorePage(QWidget* parent)
     m_backupManager = new BackupManager( QLatin1String("org.kde.nepomuk.services.nepomukbackupsync"),
                                          "/backupmanager",
                                          QDBusConnection::sessionBus(), this);
-    m_identifier = new Identifier( QLatin1String("org.kde.nepomuk.services.nepomukbackupsync"),
-                                   QLatin1String("/identifier"),
-                                   QDBusConnection::sessionBus(), this );
+    m_identifier = Identifier::instance();
 
     connect( m_identifier, SIGNAL(identificationDone(int,int)),
              this, SLOT(slotIdentificationDone(int,int)) );
@@ -198,11 +195,12 @@ void Nepomuk::RestorePage::initializePage()
 {
     QString backupUrl = field("backupToRestorePath").toString();
     kDebug() << "Restoring : " << backupUrl;
-    
-    QDBusPendingReply< int > reply = m_backupManager->restore( backupUrl );
-    reply.waitForFinished();
-    if( !reply.isError() )
-        m_id = reply.value();
+
+
+    if( backupUrl.isEmpty() )
+        backupUrl = KStandardDirs::locateLocal( "data", "nepomuk/backupsync/backup" );
+
+    m_id = Identifier::instance()->process( SyncFile(backupUrl) );
 
     if( m_id == -1 ) {
         //FIXME: This isn't implemented in the service. It's just there so that we have a
@@ -229,12 +227,8 @@ int Nepomuk::RestorePage::nextId() const
 
 bool Nepomuk::RestorePage::validatePage()
 {
-    if( m_identifier->isValid() ) {
-        m_identifier->completeIdentification( m_id );
-        return true;
-    }
-
-    return false;
+    m_identifier->completeIdentification( m_id );
+    return true;
 }
 
 void Nepomuk::RestorePage::slotIdentificationDone(int id, int unidentified)
@@ -287,13 +281,8 @@ Nepomuk::RestoreFinalPage::RestoreFinalPage(QWidget* parent): QWizardPage(parent
 {
     setupUi( this );
     setCommitPage( true );
-    m_merger = new Merger( QLatin1String("org.kde.nepomuk.services.nepomukbackupsync"),
-                           QLatin1String("/merger"),
-                           QDBusConnection::sessionBus(), this );
-
-    if( m_merger->isValid() ) {
-        connect( m_merger, SIGNAL(completed(int)), this, SLOT(slotDone(int)) );
-    }
+    m_merger = Merger::instance();
+    connect( m_merger, SIGNAL(completed(int)), this, SLOT(slotDone(int)) );
 
     m_progressBar->setMinimum( 0 );
     m_progressBar->setMaximum( 100 );
diff --git a/nepomuk/services/backupsync/gui/backupwizardpages.h b/nepomuk/services/backupsync/gui/backupwizardpages.h
index 8f3edbb..ab64145 100644
--- a/nepomuk/services/backupsync/gui/backupwizardpages.h
+++ b/nepomuk/services/backupsync/gui/backupwizardpages.h
@@ -2,7 +2,7 @@
     This file is part of the Nepomuk KDE project.
     Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
     Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
-    
+
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
     License as published by the Free Software Foundation; either
@@ -10,12 +10,12 @@
     later version accepted by the membership of KDE e.V. (or its
     successor approved by the membership of KDE e.V.), which shall
     act as a proxy defined in Section 6 of version 3 of the license.
-    
+
     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
     Lesser General Public License for more details.
-    
+
     You should have received a copy of the GNU Lesser General Public
     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
@@ -30,8 +30,8 @@
 #include <QtGui/QListWidget>
 
 #include "backupmanagerinterface.h"
-#include "mergerinterface.h"
-#include "identifierinterface.h"
+#include "merger.h"
+#include "identifier.h"
 
 #include "ui_intropage.h"
 #include "ui_backuppage.h"
@@ -43,11 +43,9 @@
 namespace Nepomuk {
 
     class BackupWizard;
-    
+
     typedef org::kde::nepomuk::services::nepomukbackupsync::BackupManager BackupManager;
-    typedef org::kde::nepomuk::services::nepomukbackupsync::Merger Merger;
-    typedef org::kde::nepomuk::services::nepomukbackupsync::Identifier Identifier;
-    
+
     class IntroPage : public QWizardPage, public Ui::IntroPage {
         Q_OBJECT
 
@@ -80,7 +78,7 @@ namespace Nepomuk {
         bool isComplete() const;
         int nextId() const;
 
-    private:        
+    private:
         BackupManager* m_backupManager;
         bool m_backupDone;
 
@@ -104,13 +102,13 @@ namespace Nepomuk {
     private slots:
         void slotSelectionChanged();
         void slotCustomBackupUrl();
-        
+
     private:
         QString m_backupFilePath;
     };
-    
+
     class IdentifierWidget;
-    
+
     class RestorePage : public QWizardPage {
         Q_OBJECT
 
@@ -123,7 +121,7 @@ namespace Nepomuk {
 
     private slots:
         void slotIdentificationDone( int id, int unidentified );
-        
+
     private:
         IdentifierWidget* m_identifierWidget;
         Identifier * m_identifier;
diff --git a/nepomuk/services/backupsync/gui/changelogmerger.cpp b/nepomuk/services/backupsync/gui/changelogmerger.cpp
new file mode 100644
index 0000000..f670d70
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/changelogmerger.cpp
@@ -0,0 +1,345 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+
+#include "changelogmerger.h"
+#include "nrio.h"
+
+#include <algorithm>
+
+#include <QtCore/QMultiHash>
+#include <QtCore/QHashIterator>
+#include <QtCore/QThread>
+
+#include <Soprano/Vocabulary/NAO>
+#include <Soprano/Vocabulary/NRL>
+#include <Soprano/Vocabulary/RDF>
+#include <Nepomuk/Vocabulary/NIE>
+#include <Nepomuk/Vocabulary/NFO>
+
+#include <Soprano/Node>
+#include <Soprano/Statement>
+#include <Soprano/Model>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/StatementIterator>
+#include <Soprano/NodeIterator>
+
+#include <Nepomuk/Resource>
+#include <Nepomuk/Variant>
+#include <Nepomuk/Types/Property>
+#include <Nepomuk/ResourceManager>
+
+#include <KDebug>
+
+int Nepomuk::ChangeLogMerger::NextId = 0;
+
+Nepomuk::ChangeLogMerger::ChangeLogMerger(Nepomuk::ChangeLog log)
+    : ResourceMerger(),
+      m_logFile( log )
+{
+    m_id = NextId++;
+}
+
+int Nepomuk::ChangeLogMerger::id()
+{
+    return m_id;
+}
+
+void Nepomuk::ChangeLogMerger::load()
+{
+    kDebug() << "Loading the ChangeLog..." << m_logFile.size();
+    m_hash = ResourceLogMap::fromChangeLog( m_logFile );
+
+    // The records are stored according to dateTime
+    Q_ASSERT( m_logFile.toList().isEmpty() == false );
+    m_minDateTime = m_logFile.toList().first().dateTime();
+}
+
+namespace {
+
+    //
+    // Cache the results. This could have very bad consequences if someone updates the ontology
+    // when the service is running
+    //
+    static QSet<KUrl> nonMergeable;
+    bool isMergeable( const KUrl & prop, Soprano::Model * model ) {
+
+        if( nonMergeable.contains( prop ) )
+            return false;
+
+        QString query = QString::fromLatin1( "ask { %1 %2 \"false\"^^xsd:boolean . }" )
+                        .arg( Soprano::Node::resourceToN3( prop ) )
+                        .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NRIO::mergeable() ) );
+
+        bool isMergeable = !model->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue();
+
+        if( !isMergeable ) {
+            nonMergeable.insert( prop );
+            return false;
+        }
+        return true;
+    }
+
+    QList<Nepomuk::ChangeLogRecord> getRecords( const Nepomuk::ResourceLogMap & hash, const KUrl resUri, const KUrl & propUri ) {
+
+        Nepomuk::ResourceLogMap::const_iterator it = hash.constFind( resUri );
+        if( it == hash.constEnd() ) {
+            return QList<Nepomuk::ChangeLogRecord>();
+        }
+
+        return it->prop.values( propUri );
+    }
+}
+
+//TODO: Add completed signal
+void Nepomuk::ChangeLogMerger::mergeChangeLog()
+{
+    m_theGraph = createGraph();
+
+    kDebug();
+    const Types::Property mergeableProperty( Nepomuk::Vocabulary::NRIO::mergeable() );
+
+    //
+    // Get own changeLog
+    //
+    kDebug() << "minDateTime : " << m_minDateTime;
+    ChangeLog ownLog;// = LogStorage::instance()->getChangeLog( m_minDateTime );
+    kDebug() << "own Log : " << ownLog.size();
+
+    // Get our and their hash
+    // ownHash = current local hash from system's ChangeLog
+    // theirHash = derived from external ChangeLog
+    ResourceLogMap ownHash = ResourceLogMap::fromChangeLog( ownLog );
+    ResourceLogMap & theirHash = m_hash;
+
+    kDebug() << "own Hash : " << ownHash.size();
+    kDebug() << "their hash : " << theirHash.size();
+
+    QHashIterator<KUrl, ResourceLog> it( theirHash );
+    while( it.hasNext() ) {
+        it.next();
+
+        // Check for resource deletions
+        if( handleResourceDeletion( it.key() ) )
+            continue;
+
+        const KUrl & resUri = it.key();
+        const ResourceLog & resLog = it.value();
+
+        kDebug() << "Resolving " << resUri;
+
+        const QList<KUrl> & properties = resLog.prop.uniqueKeys();
+        foreach( const KUrl & propUri, properties ) {
+            kDebug() << propUri;
+
+            if( !isMergeable( propUri, model() ) ) {
+                kDebug() << propUri << " is non Mergeable - IGNORING";
+                continue;
+            }
+
+            Nepomuk::Types::Property prop( propUri );
+            int cardinality = prop.maxCardinality();
+
+            QList<ChangeLogRecord> theirRecords = resLog.prop.values( propUri );
+            QList<ChangeLogRecord> ownRecords = getRecords( ownHash, resUri, propUri );
+            //kDebug() << "own Records : " << ownRecords.size();
+
+            // This case shouldn't ever happen, but just to be sure
+            if( theirRecords.empty() )
+                continue;
+
+            if( cardinality == 1 ) {
+                resolveSingleCardinality( theirRecords, ownRecords );
+            }
+            else {
+                resolveMultipleCardinality( theirRecords, ownRecords );
+            }
+        }
+
+        //if( !rs.propHash.isEmpty() )
+        //    m_jobs.append( rs );
+    }
+    //theirHash.clear();
+    //kDebug() << "Done with merge resolution : " << m_jobs.size();
+
+    //processJobs();
+}
+
+
+namespace {
+
+    Nepomuk::ChangeLogRecord maxRecord( const QList<Nepomuk::ChangeLogRecord> & records ) {
+        QList<Nepomuk::ChangeLogRecord>::const_iterator it = std::max_element( records.begin(), records.end() );
+        if( it != records.constEnd() )
+            return *it;
+        return Nepomuk::ChangeLogRecord();
+    }
+}
+
+
+void Nepomuk::ChangeLogMerger::resolveSingleCardinality(const QList< Nepomuk::ChangeLogRecord >& theirRecords, const QList< Nepomuk::ChangeLogRecord >& ownRecords)
+{
+    kDebug() << "O: " << ownRecords.size() << " " << "T:" << theirRecords.size();
+
+    //Find max on the basis of time stamp
+    ChangeLogRecord theirMax = maxRecord( theirRecords );
+    ChangeLogRecord ownMax = maxRecord( ownRecords );
+    kDebug() << "TheirMax : "<< theirMax.toString();
+    kDebug() << "OwnMax " << ownMax.toString();
+
+    if( theirMax > ownMax ) {
+        Soprano::Statement statement( theirMax.st().subject(), theirMax.st().predicate(),
+                                      Soprano::Node(), Soprano::Node() );
+
+        if( theirMax.added() ) {
+            Soprano::Node object = theirMax.st().object();
+            kDebug() << "Resolved - Adding " << object;
+
+            if( !model()->containsAnyStatement( statement ) ) {
+                statement.setObject( object );
+                statement.setContext( m_theGraph );
+                model()->addStatement( statement );
+            }
+        }
+        else {
+            kDebug() << "Resolved - Removing";
+            model()->removeAllStatements( statement );
+        }
+    }
+}
+
+namespace {
+
+    struct MergeData {
+        bool added;
+        QDateTime dateTime;
+
+        MergeData( bool add, const QDateTime & dt )
+            : added( add ),
+              dateTime( dt )
+        {}
+    };
+
+
+}
+
+void Nepomuk::ChangeLogMerger::resolveMultipleCardinality( const QList<Nepomuk::ChangeLogRecord>& theirRecords, const QList<Nepomuk::ChangeLogRecord>& ownRecords)
+{
+    kDebug() << "MULTIPLE";
+    kDebug() << "O: " << ownRecords.size() << " " << "T:" << theirRecords.size();
+
+    const Soprano::Statement& reference = theirRecords.first().st();
+    Soprano::Statement baseStatement( reference.subject(), reference.predicate(), Soprano::Node(), Soprano::Node() );
+
+    //
+    // Merge both record lists
+    //
+    //TODO: Optimize merging - use merge sort or something equivilant
+    QList<ChangeLogRecord> records = ownRecords;
+    records << theirRecords;
+    qSort( records );
+
+    QHash<Soprano::Node, MergeData> hash;
+    foreach( const ChangeLogRecord rec, records ) {
+        Soprano::Node object = rec.st().object();
+        QHash<Soprano::Node, MergeData>::const_iterator it = hash.constFind( object );
+        if( it == hash.constEnd() ) {
+            hash.insert( object, MergeData( rec.added(), rec.dateTime() ) );
+        }
+        else {
+            // +ve after -ve
+            if( rec.added() == true && it.value().added == false ) {
+                hash.remove( object );
+                hash.insert( object, MergeData( rec.added(), rec.dateTime() ) );
+            }
+            // -ve after +ve
+            else if( rec.added() == false && it.value().added == true ) {
+                hash.remove( object );
+            }
+            // +ve after +ve
+            // -ve after -ve
+            //    Do nothing
+        }
+    }
+
+    //
+    // Do the actual merging
+    //
+    QHashIterator<Soprano::Node, MergeData> it( hash );
+    while( it.hasNext() ) {
+        it.next();
+
+        Soprano::Statement st( baseStatement );
+        st.setObject( it.key() );
+
+        MergeData data = it.value();
+        if( data.added == true ) {
+            if( !model()->containsAnyStatement( st ) ) {
+                st.setContext( m_theGraph );
+                model()->addStatement( st );
+                kDebug() << "adding - " << st;
+            }
+        }
+        else {
+            kDebug() << "removing " << st;
+            model()->removeAllStatements( st );
+        }
+    }
+
+    m_multipleMergers.append( Soprano::Statement( baseStatement.subject(),
+                                                  baseStatement.predicate(),
+                                                  Soprano::Node() ) );
+}
+
+QList< Soprano::Statement > Nepomuk::ChangeLogMerger::multipleMergers() const
+{
+    return m_multipleMergers;
+}
+
+bool Nepomuk::ChangeLogMerger::handleResourceDeletion(const KUrl& resUri)
+{
+    ResourceLog & log = m_hash[ resUri ];
+    const KUrl& rdfTypeProp = Soprano::Vocabulary::RDF::type();
+
+    QList<ChangeLogRecord> records = log.prop.values( rdfTypeProp );
+    if( records.empty() )
+        return false;
+
+    //
+    // Check if rdf:type is being removed
+    //
+    bool removed = false;
+    foreach( const ChangeLogRecord & r, records ) {
+        if( !r.added() ) {
+            removed = true;
+            break;
+        }
+    }
+    if( !removed )
+        return false;
+
+    // If removed, remove all records and delete the resource
+    m_hash.remove( resUri );
+    Resource res( resUri );
+    res.remove();
+    return true;
+}
diff --git a/nepomuk/services/backupsync/gui/changelogmerger.h b/nepomuk/services/backupsync/gui/changelogmerger.h
new file mode 100644
index 0000000..3d1ca42
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/changelogmerger.h
@@ -0,0 +1,88 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+
+#ifndef CHANGELOGMERGER_H
+#define CHANGELOGMERGER_H
+
+#include "resourcemerger.h"
+#include "syncresource.h"
+#include "changelog.h"
+#include "resourcelog.h"
+
+#include <QtCore/QDateTime>
+
+namespace Nepomuk {
+    class ChangeLogMerger : public Sync::ResourceMerger
+    {
+    public:
+        ChangeLogMerger( ChangeLog log );
+
+        int id();
+        void mergeChangeLog();
+
+        /**
+         * Converts the contents of the LogFile into the MergeHash
+         * The LogFile can get rather large, so this could be a time consuming
+         * process.
+         */
+        void load();
+
+        QList<Soprano::Statement> multipleMergers() const;
+        
+    private:
+        ChangeLog m_logFile;
+        QDateTime m_minDateTime;
+        
+        QList<Sync::SyncResource> m_jobs;
+        QList<Soprano::Statement> m_multipleMergers;
+        
+        /// Contains all the records from the LogFile
+        ResourceLogMap m_hash;
+        
+        static int NextId;
+        int m_id;
+
+        KUrl m_theGraph;
+
+        /**
+         * Handles the case when the Resource's metadata has been deleted from
+         * the LogFile.
+         * \return true if resUri was deleted, false otherwsie
+         */
+        bool handleResourceDeletion( const KUrl & resUri );
+
+        /**
+         * Resolve conflicts for properties with multiple cardinalities. It works by locally
+         * making all the changes in the SyncResource.
+         */
+        void resolveMultipleCardinality( const QList<ChangeLogRecord >& theirRecords, const QList<ChangeLogRecord >& ownRecords );
+        
+        /**
+         * Same as multiple Cardinality resolution, but a lot faster. It figures out which statement
+         * should be present by looking at the max time stamp.
+         */
+        void resolveSingleCardinality( const QList< Nepomuk::ChangeLogRecord >& theirRecords, const QList< Nepomuk::ChangeLogRecord >& ownRecords );
+    };
+
+}
+#endif // CHANGELOGMERGER_H
diff --git a/nepomuk/services/backupsync/gui/identifier.cpp b/nepomuk/services/backupsync/gui/identifier.cpp
new file mode 100644
index 0000000..b4e9ccf
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/identifier.cpp
@@ -0,0 +1,264 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "changelog.h"
+#include "identifier.h"
+
+#include <QtDBus/QDBusConnection>
+
+#include <QtCore/QMutexLocker>
+#include <QtCore/QHashIterator>
+#include <QtCore/QFile>
+
+#include <Soprano/Model>
+#include <Soprano/Graph>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/Vocabulary/RDF>
+#include <Soprano/Serializer>
+#include <Soprano/PluginManager>
+#include <Soprano/Util/SimpleStatementIterator>
+
+#include <Nepomuk/ResourceManager>
+#include <Nepomuk/Resource>
+#include <Nepomuk/Variant>
+#include <Nepomuk/Vocabulary/NIE>
+
+#include <KDebug>
+
+Nepomuk::Identifier::Identifier(QObject* parent): QThread(parent)
+{
+    //Register DBus interface
+    //new IdentifierAdaptor( this );
+    //QDBusConnection dbus = QDBusConnection::sessionBus();
+    //dbus.registerObject( QLatin1String("/identifier"), this );
+
+    start();
+}
+
+Nepomuk::Identifier* Nepomuk::Identifier::instance()
+{
+    static Identifier ident;
+    return &ident;
+}
+
+Nepomuk::Identifier::~Identifier()
+{
+    stop();
+    quit();
+}
+
+
+int Nepomuk::Identifier::process(const Nepomuk::SyncFile& sf)
+{
+    m_queueMutex.lock();
+
+    SyncFileIdentifier* identifier = new SyncFileIdentifier( sf );
+    int id = identifier->id();
+    m_queue.enqueue( identifier );
+
+    m_queueMutex.unlock();
+    m_queueWaiter.wakeAll();
+
+    kDebug() << "Processing ID : " << id;
+    return id;
+}
+
+
+void Nepomuk::Identifier::stop()
+{
+    m_stopped = true;
+    m_queueWaiter.wakeAll();
+}
+
+
+
+void Nepomuk::Identifier::run()
+{
+    m_stopped = false;
+
+    while( !m_stopped ) {
+
+        // lock for initial iteration
+        m_queueMutex.lock();
+
+        while( !m_queue.isEmpty() ) {
+
+            SyncFileIdentifier* identifier = m_queue.dequeue();
+
+            // unlock after queue utilization
+            m_queueMutex.unlock();
+
+            identifier->load();
+            identifyAllWithCompletedSignals( identifier );
+
+            emit identificationDone( identifier->id(), identifier->unidentified().size() );
+
+            m_processMutex.lock();
+            m_processes[ identifier->id() ] = identifier;
+            m_processMutex.unlock();
+
+            // Send the sigals
+            foreach( const KUrl & uri, identifier->unidentified() ) {
+                emitNotIdentified( identifier->id(), identifier->statements( uri ).toList() );
+            }
+
+            foreach( const KUrl & uri, identifier->mappings().uniqueKeys() ) {
+                emit identified( identifier->id(), uri.url(), identifier->mappedUri( uri ).url() );
+            }
+
+//             if( identifier->allIdentified() ) {
+//                 m_processes.remove( identifier->id() );
+//                 delete identifier;
+//             }
+
+            m_queueMutex.lock();
+        }
+
+        // wait for more input
+        kDebug() << "Waiting...";
+        m_queueWaiter.wait( &m_queueMutex );
+        m_queueMutex.unlock();
+        kDebug() << "Woke up.";
+    }
+}
+
+
+bool Nepomuk::Identifier::identify(int id, const QString& oldUriString, const QString& newUriString)
+{
+    QUrl oldUri( oldUriString );
+    QUrl newUri( newUriString );
+
+    kDebug() << newUri;
+    // Lock the mutex and all
+    QMutexLocker lock ( &m_processMutex );
+
+    QHash<int, SyncFileIdentifier*>::iterator it = m_processes.find( id );
+    if( it == m_processes.end() )
+        return false;
+
+    SyncFileIdentifier* ip = *it;
+
+    if ( oldUri.scheme() != QLatin1String("nepomuk") )
+        return false;
+
+    if( newUri.scheme() == QLatin1String("nepomuk") ) {
+        ip->forceResource( oldUri, Nepomuk::Resource(newUri) );
+    }
+    else if( newUri.scheme() == "file" ) {
+        ip->forceResource( oldUri, Nepomuk::Resource(newUri) );
+    }
+
+    m_queueMutex.lock();
+    m_queue.enqueue( ip );
+    m_queueMutex.unlock();
+    m_queueWaiter.wakeAll();
+
+    return true;
+}
+
+
+bool Nepomuk::Identifier::ignore(int id, const QString& urlString, bool ignoreSub)
+{
+    KUrl url( urlString );
+    // Lock the mutex and all
+    QMutexLocker lock ( &m_processMutex );
+
+    QHash<int, SyncFileIdentifier*>::iterator it = m_processes.find( id );
+    if( it == m_processes.end() )
+        return false;
+
+    SyncFileIdentifier* identifier = *it;
+    return identifier->ignore( url, ignoreSub );
+}
+
+void Nepomuk::Identifier::ignoreAll(int id)
+{
+    QMutexLocker lock ( &m_processMutex );
+
+    QHash<int, SyncFileIdentifier*>::iterator it = m_processes.find( id );
+    if( it == m_processes.end() )
+        return;
+
+    SyncFileIdentifier* identifier = *it;
+    foreach( const KUrl & url, identifier->unidentified() ) {
+        identifier->ignore( url, true );
+    }
+}
+
+void Nepomuk::Identifier::emitNotIdentified(int id, const QList< Soprano::Statement >& stList)
+{
+    const Soprano::Serializer* serializer = Soprano::PluginManager::instance()->discoverSerializerForSerialization( Soprano::SerializationNQuads );
+
+    Soprano::Util::SimpleStatementIterator it( stList );
+    QString ser;
+    QTextStream stream( &ser );
+    serializer->serialize( it, stream, Soprano::SerializationNQuads );
+
+    emit notIdentified( id, ser );
+}
+
+void Nepomuk::Identifier::test()
+{
+    kDebug() << "Test!";
+}
+
+void Nepomuk::Identifier::completeIdentification(int id)
+{
+    kDebug() << id;
+
+    QMutexLocker lock ( &m_processMutex );
+
+    QHash<int, SyncFileIdentifier*>::iterator it = m_processes.find( id );
+    if( it == m_processes.end() )
+        return;
+
+    SyncFileIdentifier* identifier = *it;
+    m_processes.remove( id );
+
+    ChangeLog log = identifier->convertedChangeLog();
+    kDebug() << "ChangeLog of size " << log.size() << " has been converted";
+    if( !log.empty() ) {
+        kDebug() << "sending ChangeLog of size : " << log.size();
+        emit processed( log );
+    }
+
+    delete identifier;
+}
+
+
+void Nepomuk::Identifier::identifyAllWithCompletedSignals(Nepomuk::SyncFileIdentifier* ident)
+{
+    int unidentified = ident->unidentified().size();
+    float step = 100.0/unidentified;
+    float progress = 0;
+
+    emit completed( ident->id(), 0 );
+    foreach( const KUrl & url, ident->unidentified() ) {
+        ident->identify( url );
+
+        progress += step;
+        emit completed( ident->id(), (int)progress );
+    }
+    emit completed( ident->id(), 100 );
+}
+
+
+#include "identifier.moc"
diff --git a/nepomuk/services/backupsync/gui/identifier.h b/nepomuk/services/backupsync/gui/identifier.h
new file mode 100644
index 0000000..0cbc0d9
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/identifier.h
@@ -0,0 +1,98 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IDENTIFICATIONTHREAD_H
+#define IDENTIFICATIONTHREAD_H
+
+#include <QtCore/QThread>
+#include <QtCore/QQueue>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QQueue>
+#include <QtCore/QUrl>
+
+#include "syncfileidentifier.h"
+
+namespace Soprano {
+    class Model;
+}
+
+namespace Nepomuk {
+
+    class SyncFile;
+    class ChangeLog;
+
+    class Identifier : public QThread
+    {
+        Q_OBJECT
+
+        Identifier( QObject* parent = 0 );
+        virtual ~Identifier();
+
+        void stop();
+        void test();
+
+    public :
+        static Identifier* instance();
+
+    Q_SIGNALS:
+        void identified( int id, const QString & oldUri, const QString & newUri );
+        void notIdentified( int id, const QString & serializedStatements );
+
+        void identificationDone( int id, int unidentified );
+        void processed( const Nepomuk::ChangeLog & logFile );
+
+        void completed( int id, int progress );
+    public Q_SLOTS:
+        int process( const SyncFile & sf );
+
+        /**
+         * Add the (oldUri, newUri) pair to the identifier list
+         * @param oldUri has to be a nepomuk:/res/
+         * @param newUri can be a nepomuk:/res/ or file:/
+         */
+        bool identify( int id, const QString & oldUri, const QString & newUri );
+
+        bool ignore(int id, const QString& url, bool ignoreSubDirectories);
+
+        void ignoreAll( int id );
+
+        void completeIdentification( int id );
+
+    protected:
+        virtual void run();
+
+    private :
+        QQueue<SyncFileIdentifier *> m_queue;
+        QMutex m_queueMutex;
+        QWaitCondition m_queueWaiter;
+
+        bool m_stopped;
+
+        QHash< int, SyncFileIdentifier *> m_processes;
+        QMutex m_processMutex;
+
+        void emitNotIdentified( int id, const QList<Soprano::Statement> & stList );
+        void identifyAllWithCompletedSignals( SyncFileIdentifier * ident );
+    };
+
+}
+#endif // IDENTIFICATIONTHREAD_H
diff --git a/nepomuk/services/backupsync/gui/identifierwidget.cpp b/nepomuk/services/backupsync/gui/identifierwidget.cpp
index 35ee834..9dbe84b 100644
--- a/nepomuk/services/backupsync/gui/identifierwidget.cpp
+++ b/nepomuk/services/backupsync/gui/identifierwidget.cpp
@@ -23,8 +23,6 @@
 #include "identifierwidget.h"
 #include "identifiermodel.h"
 #include "identifiermodeltree.h"
-#include "identifierinterface.h"
-#include "../service/dbusoperators.h"
 
 #include <QtGui/QLabel>
 #include <QtGui/QVBoxLayout>
@@ -39,27 +37,23 @@
 
 Nepomuk::IdentifierWidget::IdentifierWidget(int id, QWidget* parent): QWidget(parent), m_id(id)
 {
-    registerMetaTypes();
+    //registerMetaTypes();
     setupUi(this);
-    
+
     m_model = new IdentifierModel( this );
 
     MergeConflictDelegate * delegate = new MergeConflictDelegate( m_viewConflicts, this );
     m_viewConflicts->setModel( m_model );
     m_viewConflicts->setItemDelegate( delegate );
 
-    m_identifier = new Identifier( QLatin1String("org.kde.nepomuk.services.nepomukbackupsync"),
-                                   QLatin1String("/identifier"),
-                                   QDBusConnection::sessionBus(), this );
-    
-    if( m_identifier->isValid() ) {
-        connect( m_identifier, SIGNAL(notIdentified(int,QString)),
-                 this, SLOT(notIdentified(int,QString)) );
-        connect( m_identifier, SIGNAL(identified(int,QString,QString)),
-                 this, SLOT(identified(int,QString,QString)) );
-        connect( m_identifier, SIGNAL(completed(int,int)),
-                 this, SLOT(completed(int,int)) );
-    }
+    m_identifier = Identifier::instance();
+
+    connect( m_identifier, SIGNAL(notIdentified(int,QString)),
+             this, SLOT(notIdentified(int,QString)) );
+    connect( m_identifier, SIGNAL(identified(int,QString,QString)),
+             this, SLOT(identified(int,QString,QString)) );
+    connect( m_identifier, SIGNAL(completed(int,int)),
+             this, SLOT(completed(int,int)) );
 
     connect( delegate, SIGNAL(requestResourceResolve(QUrl)),
              this, SLOT(identify()) );
@@ -73,14 +67,14 @@ Nepomuk::IdentifierWidget::IdentifierWidget(int id, QWidget* parent): QWidget(pa
 void Nepomuk::IdentifierWidget::ignore(const QUrl& uri)
 {
     Q_UNUSED( uri );
-    
+
     QModelIndex index = m_viewConflicts->currentIndex();
     if( !index.isValid() )
         return;
 
     IdentifierModelTreeItem * item = static_cast<IdentifierModelTreeItem*>( index.internalPointer() );
     item->setDiscarded( true );
-    
+
     m_identifier->ignore( m_id, item->resourceUri().toString(), true);
 }
 
@@ -107,7 +101,7 @@ void Nepomuk::IdentifierWidget::notIdentified(int id, const QString& string)
 {
     if( id != m_id )
         return;
-    
+
     kDebug() << string;
     const Soprano::Parser* parser = Soprano::PluginManager::instance()->discoverParserForSerialization( Soprano::SerializationNQuads );
 
diff --git a/nepomuk/services/backupsync/gui/identifierwidget.h b/nepomuk/services/backupsync/gui/identifierwidget.h
index e737ffc..bcb5b27 100644
--- a/nepomuk/services/backupsync/gui/identifierwidget.h
+++ b/nepomuk/services/backupsync/gui/identifierwidget.h
@@ -28,10 +28,9 @@
 #include <QPushButton>
 
 #include "mergeconflictdelegate.h"
+#include "identifier.h"
 #include "ui_mergeconflictwidget.h"
 
-class OrgKdeNepomukServicesNepomukbackupsyncIdentifierInterface;
-
 namespace Nepomuk {
 
     class IdentifierModel;
@@ -45,18 +44,17 @@ namespace Nepomuk {
     private slots:
         void identify(  );
         void ignore( const QUrl & uri );
-        
+
     private slots:
         void notIdentified( int id, const QString & string );
         void identified( int id, const QString & oldUri, const QString & newUri );
         void completed( int id, int progress );
         void slotDiscardAll();
-        
+
     private :
         IdentifierModel * m_model;
         int m_id;
 
-        typedef OrgKdeNepomukServicesNepomukbackupsyncIdentifierInterface Identifier;
         Identifier * m_identifier;
     };
 
diff --git a/nepomuk/services/backupsync/gui/merger.cpp b/nepomuk/services/backupsync/gui/merger.cpp
new file mode 100644
index 0000000..8caea89
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/merger.cpp
@@ -0,0 +1,135 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "merger.h"
+
+#include "changelog.h"
+#include "syncfile.h"
+
+#include <QtDBus/QDBusConnection>
+
+#include <KDebug>
+
+Nepomuk::Merger::Merger( QObject* parent )
+    : QThread( parent )
+{
+    //Register DBus interface
+    //new MergerAdaptor( this );
+    //QDBusConnection dbus = QDBusConnection::sessionBus();
+    //dbus.registerObject( QLatin1String("/merger"), this );
+
+    start();
+}
+
+Nepomuk::Merger* Nepomuk::Merger::instance()
+{
+    static Merger m;
+    return &m;
+}
+
+
+Nepomuk::Merger::~Merger()
+{
+    stop();
+    wait();
+}
+
+
+void Nepomuk::Merger::stop()
+{
+    m_stopped = true;
+    m_queueWaiter.wakeAll();
+}
+
+
+int Nepomuk::Merger::process(const Nepomuk::ChangeLog& changeLog )
+{
+    kDebug();
+    m_queueMutex.lock();
+
+    kDebug() << "Received ChangeLog -- " << changeLog.size();
+    ChangeLogMerger * request = new ChangeLogMerger( changeLog );
+    m_queue.enqueue( request );
+
+    m_queueMutex.unlock();
+    m_queueWaiter.wakeAll();
+
+    return request->id();
+}
+
+
+void Nepomuk::Merger::run()
+{
+    m_stopped = false;
+
+    while( !m_stopped ) {
+
+        // lock for initial iteration
+        m_queueMutex.lock();
+
+        while( !m_queue.isEmpty() ) {
+
+            ChangeLogMerger* request = m_queue.dequeue();
+            //kDebug() << "Processing request #" << request->id() << " with size " << request->size();
+
+            // unlock after queue utilization
+            m_queueMutex.unlock();
+
+            //FIXME: Fake completed signals!
+            emit completed( 5 );
+            request->load();
+            emit completed( 55 );
+            request->mergeChangeLog();
+            emit completed( 100 );
+
+            /*
+            if( !request->done() ) {
+                QMutexLocker lock( &m_processMutex );
+                m_processes[ request->id() ] = request;
+            }
+
+            disconnect( request, SIGNAL(completed(int)),
+                        this, SIGNAL(completed(int)) );
+            disconnect( request, SIGNAL(multipleMerge(QString,QString)),
+                     this, SIGNAL(multipleMerge(QString,QString)) );
+
+            if( request->done() ){*/
+
+            foreach( const Soprano::Statement & st, request->multipleMergers() ) {
+                emit multipleMerge( st.subject().uri().toString(),
+                                    st.predicate().uri().toString() );
+            }
+
+            m_processes.remove( request->id() );
+            delete request;
+
+            m_queueMutex.lock();
+        }
+
+        kDebug() << "Waiting...";
+        m_queueWaiter.wait( &m_queueMutex );
+        m_queueMutex.unlock();
+        kDebug() << "Woke up.";
+    }
+}
+
+
+#include "merger.moc"
diff --git a/nepomuk/services/backupsync/gui/merger.h b/nepomuk/services/backupsync/gui/merger.h
new file mode 100644
index 0000000..74efcf7
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/merger.h
@@ -0,0 +1,76 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MERGETHREAD_H
+#define MERGETHREAD_H
+
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QUrl>
+#include <QtCore/QQueue>
+
+#include "changelogmerger.h"
+
+namespace Soprano {
+    class Model;
+}
+
+namespace Nepomuk {
+
+    class ResourceManager;
+    class ChangeLog;
+
+    class Merger : public QThread
+    {
+        Q_OBJECT
+
+        Merger( QObject* parent = 0 );
+        virtual ~Merger();
+
+        void stop();
+
+    public :
+        static Merger* instance();
+
+    public Q_SLOTS:
+        int process( const Nepomuk::ChangeLog & logFile );
+
+    Q_SIGNALS:
+        void completed( int percent );
+        void multipleMerge( const QString & uri, const QString & prop );
+
+    protected:
+        virtual void run();
+
+    private:
+        QQueue<ChangeLogMerger*> m_queue;
+        QMutex m_queueMutex;
+        QWaitCondition m_queueWaiter;
+
+        bool m_stopped;
+
+        QHash<int, ChangeLogMerger*> m_processes;
+        QMutex m_processMutex;
+    };
+
+}
+#endif // MERGETHREAD_H
diff --git a/nepomuk/services/backupsync/gui/nepomukbackup.desktop b/nepomuk/services/backupsync/gui/nepomukbackup.desktop
index ab1e794..cf54d99 100644
--- a/nepomuk/services/backupsync/gui/nepomukbackup.desktop
+++ b/nepomuk/services/backupsync/gui/nepomukbackup.desktop
@@ -1,6 +1,7 @@
 [Desktop Entry]
 Name=Nepomuk Backup
 Name[bg]=Модул за архивиране Nepomuk
+Name[bs]=Rezerva Nepomuka
 Name[ca]=Còpia de seguretat del Nepomuk
 Name[ca@valencia]=Còpia de seguretat del Nepomuk
 Name[cs]=Záloha Nepomuku
@@ -18,6 +19,7 @@ Name[hi]=नेपोमक बेकप
 Name[hr]=Nepomukova sigurnosna kopija
 Name[hu]=Nepomuk mentés
 Name[ia]=Retrocopia de Nepomuk
+Name[is]=Nepomuk öryggisafritun
 Name[it]=Copia di sicurezza di Nepomuk
 Name[ja]=Nepomuk バックアップ
 Name[kk]=Nepomuk сақтық көшірмелеу
@@ -25,10 +27,11 @@ Name[km]=ការ​​បម្រុង​ទុក​របស់ Nepomuk
 Name[kn]=ನೆಪೋಮುಕ್ ಬ್ಯಾಕ್‌ಅಪ್
 Name[ko]=Nepomuk 백업
 Name[lt]=Nepomuk atsarginė kopija
-Name[mai]=नेपोमक बेकप
+Name[lv]=Nepomuk rezerves kopiju veidošana
 Name[nb]=Nepomuk sikringskopi
 Name[nds]=Nepomuk-Sekerheitkopie
 Name[nl]=Nepomuk reservekopie maken
+Name[nn]=Reservekopi av Nepomuk
 Name[pa]=ਨਿਪੋਮੁਕ ਬੈਕਅੱਪ
 Name[pl]=Kopia zapasowa Nepomuka
 Name[pt]=Salvaguarda do Nepomuk
@@ -44,8 +47,10 @@ Name[sr@latin]=Rezerva Nepomuka
 Name[sv]=Nepomuk säkerhetskopiering
 Name[th]=สำรองข้อมูลบริการ Neomuk
 Name[tr]=Nepomuk Yedekleme
+Name[ug]=Nepomuk زاپاسلاش
 Name[uk]=Резервні копії даних Nepomuk
 Name[x-test]=xxNepomuk Backupxx
+Name[zh_CN]=Nepomuk 备份
 Name[zh_TW]=Nepomuk 備份
 Exec=nepomukbackup
 Icon=nepomuk
diff --git a/nepomuk/services/backupsync/gui/resourcelog.cpp b/nepomuk/services/backupsync/gui/resourcelog.cpp
new file mode 100644
index 0000000..a24d3f0
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/resourcelog.cpp
@@ -0,0 +1,143 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "resourcelog.h"
+#include "changelog.h"
+#include "changelogrecord.h"
+
+#include <QtCore/QMultiHash>
+
+#include <Nepomuk/Types/Property>
+#include <algorithm>
+
+
+Nepomuk::ResourceLogMap Nepomuk::ResourceLogMap::fromChangeLogRecordList(const QList<ChangeLogRecord>& records)
+{
+    ResourceLogMap hash;
+
+    //
+    // Organize according to resource uri
+    //
+    QMultiHash<KUrl, ChangeLogRecord> multiHash;
+    foreach( const ChangeLogRecord &r, records ) {
+        multiHash.insert( r.st().subject().uri(), r );
+    }
+
+    //
+    // Convert to MergeHash
+    //
+    const QList<KUrl> & keys = multiHash.uniqueKeys();
+    foreach( const KUrl & key, keys ) {
+        ResourceLog ms;
+        ms.uri = key;
+
+        const QList<ChangeLogRecord>& records = multiHash.values( key );
+        foreach( const ChangeLogRecord & r, records ) {
+            const KUrl &pred = r.st().predicate().uri();
+            ms.prop.insert( pred, r );
+        }
+        hash.insert( ms.uri, ms );
+    }
+
+    return hash;
+}
+
+
+Nepomuk::ResourceLogMap Nepomuk::ResourceLogMap::fromChangeLog(const Nepomuk::ChangeLog& log)
+{
+    return fromChangeLogRecordList( log.toList() );
+}
+
+
+namespace {
+
+    Nepomuk::ChangeLogRecord maxRecord( const QList<Nepomuk::ChangeLogRecord> & records ) {
+        QList<Nepomuk::ChangeLogRecord>::const_iterator it = std::max_element( records.begin(), records.end() );
+        if( it != records.constEnd() )
+            return *it;
+        return Nepomuk::ChangeLogRecord();
+    }
+
+    typedef QHash<Soprano::Node, Nepomuk::ChangeLogRecord> OptimizeHash;
+
+}
+
+
+void Nepomuk::ResourceLogMap::optimize()
+{
+    QMutableHashIterator<KUrl, ResourceLog> it( *this );
+    while( it.hasNext() ) {
+        it.next();
+
+        ResourceLog & log = it.value();
+
+        const QList<KUrl> & properties = log.prop.uniqueKeys();
+        foreach( const KUrl & propUri, properties ) {
+            QList<ChangeLogRecord> records = log.prop.values( propUri );
+
+            Types::Property property( propUri );
+            int maxCard = property.maxCardinality();
+
+            if( maxCard == 1 ) {
+                ChangeLogRecord max = maxRecord( records );
+                records.clear();
+                records.append( max );
+            }
+            else {
+                // The records have to be sorted by timeStamp in order to optimize them
+                qSort( records );
+
+                OptimizeHash hash;
+                foreach( const ChangeLogRecord & record, records ) {
+                    if( record.added() ) {
+                        OptimizeHash::const_iterator iter = hash.constFind( record.st().object() );
+                        if( iter != hash.constEnd() ){
+                            if( !iter.value().added() ) {
+                                hash.remove( record.st().object() );
+                            }
+                        }
+                        else {
+                            hash.insert( record.st().object(), record );
+                        }
+                    }
+                    else {
+                        OptimizeHash::const_iterator iter = hash.constFind( record.st().object() );
+                        if( iter != hash.constEnd() ) {
+                            if( iter.value().added() ) {
+                                hash.remove( record.st().object() );
+                            }
+                        }
+                        else
+                            hash.insert( record.st().object(), record );
+                    }
+                }
+                records = hash.values();
+                qSort( records );
+
+                // Update the log
+                log.prop.remove( propUri );
+                foreach( const ChangeLogRecord & record, records )
+                    log.prop.insert( propUri, record );
+            }
+        }
+
+    }
+}
diff --git a/nepomuk/services/backupsync/gui/resourcelog.h b/nepomuk/services/backupsync/gui/resourcelog.h
new file mode 100644
index 0000000..fde6dc5
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/resourcelog.h
@@ -0,0 +1,53 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef RESOURCELOG_H
+#define RESOURCELOG_H
+
+#include <QtCore/QUrl>
+#include <QtCore/QMultiHash>
+
+#include <KUrl>
+
+#include "changelog.h"
+#include "changelogrecord.h"
+
+namespace Nepomuk {
+
+        
+        class ResourceLog
+        {
+        public:
+            KUrl uri;
+            QMultiHash<KUrl, ChangeLogRecord> prop;
+        };
+
+        class ResourceLogMap : public QHash<KUrl, ResourceLog> {
+        public:
+            static ResourceLogMap fromChangeLogRecordList( const QList<ChangeLogRecord> & records );
+            static ResourceLogMap fromChangeLog( const Nepomuk::ChangeLog& log );
+
+            void optimize();
+        };
+    
+}
+#endif // RESOURCELOG_H
diff --git a/nepomuk/services/backupsync/gui/syncfileidentifier.cpp b/nepomuk/services/backupsync/gui/syncfileidentifier.cpp
new file mode 100644
index 0000000..1eb1db5
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/syncfileidentifier.cpp
@@ -0,0 +1,205 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "syncfileidentifier.h"
+#include "changelogrecord.h"
+#include "syncresource.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+
+#include <KDebug>
+
+#include <Nepomuk/Resource>
+#include <Nepomuk/Variant>
+#include <Nepomuk/Vocabulary/NIE>
+
+int Nepomuk::SyncFileIdentifier::NextId = 0;
+
+Nepomuk::SyncFileIdentifier::SyncFileIdentifier(const Nepomuk::SyncFile& sf)
+    : ResourceIdentifier()
+{
+    m_id = NextId++;
+
+    m_changeLog = sf.changeLog();
+    m_identificationSet = sf.identificationSet();
+
+    //kDebug() << "Identification Set - ";
+    //kDebug() << m_identificationSet.toList();
+}
+
+Nepomuk::SyncFileIdentifier::~SyncFileIdentifier()
+{
+    // What do we do over here?
+}
+
+namespace {
+
+    //
+    // Removes the old home directory and replaces it with the current one
+    // TODO: Make it OS independent
+    //
+    QUrl translateHomeUri( const QUrl & uri ) {
+        QString uriString = uri.toString();
+
+        QRegExp regEx("^file://(/home/[^/]*)(/.*)$");
+        if( regEx.exactMatch( uriString ) ) {
+            QString newUriString = "file://" + QDir::homePath() + regEx.cap(2);
+
+            uriString.replace( regEx, newUriString );
+            return QUrl( newUriString );
+        }
+        return uri;
+    }
+}
+
+void Nepomuk::SyncFileIdentifier::load()
+{
+    if( unidentified().size() > 0 )
+        return;
+
+    //
+    // Translate all file:// uri so that the home dir is correct.
+    //
+    QList<Soprano::Statement> identList = m_identificationSet.toList();
+    QMutableListIterator<Soprano::Statement> it( identList );
+    while( it.hasNext() ) {
+        it.next();
+
+        Soprano::Statement & st = it.value();
+        if( st.object().isResource() && st.object().uri().scheme() == QLatin1String("file") )
+            st.setObject( Soprano::Node( translateHomeUri( st.object().uri() ) ) );
+    }
+
+    //kDebug() << "After translation : ";
+    //kDebug() << identList;
+
+    addStatements( identList );
+}
+
+
+Nepomuk::ChangeLog Nepomuk::SyncFileIdentifier::convertedChangeLog()
+{
+    QList<ChangeLogRecord> masterLogRecords = m_changeLog.toList();
+    kDebug() << "masterLogRecords : " << masterLogRecords.size();
+
+    QList<ChangeLogRecord> identifiedRecords;
+    QMutableListIterator<ChangeLogRecord> it( masterLogRecords );
+
+    while( it.hasNext() ) {
+        ChangeLogRecord r = it.next();
+
+        // Identify Subject
+        KUrl subUri = r.st().subject().uri();
+        if( subUri.scheme() == QLatin1String("nepomuk") ) {
+            KUrl newUri = mappedUri( subUri );
+            if( newUri.isEmpty() )
+                continue;
+
+            r.setSubject( newUri );
+        }
+
+        // Identify object
+        if( r.st().object().isResource() ) {
+            KUrl objUri = r.st().object().uri();
+            if( objUri.scheme() == QLatin1String("nepomuk") ) {
+                KUrl newUri = mappedUri( objUri );
+                if( newUri.isEmpty() )
+                    continue;
+
+                r.setObject( newUri );
+            }
+        }
+
+        identifiedRecords.push_back( r );
+
+        // Remove the statement from the masterchangerecords
+        it.remove();
+    }
+
+    // Update the master change log
+    m_changeLog = ChangeLog::fromList( masterLogRecords );
+
+    return ChangeLog::fromList( identifiedRecords );
+}
+
+void Nepomuk::SyncFileIdentifier::identifyAll()
+{
+    Nepomuk::Sync::ResourceIdentifier::identifyAll();
+}
+
+int Nepomuk::SyncFileIdentifier::id()
+{
+    return m_id;
+}
+
+
+Nepomuk::Resource Nepomuk::SyncFileIdentifier::createNewResource(const Sync::SyncResource & simpleRes) const
+{
+    kDebug();
+    Nepomuk::Resource res;
+
+    if( simpleRes.isFileDataObject() ) {
+        res = Nepomuk::Resource( simpleRes.nieUrl() );
+        if( res.exists() ) {
+            // If the resource already exists. We should not create it. This is to avoid the bug where
+            // a different file with the same nie:url exists. If it was the same file, identification
+            // should have found it. If it hasn't, well tough luck. No other option but to manually
+            // identify
+            return Resource();
+        }
+    }
+
+    const QList<KUrl> & keys = simpleRes.uniqueKeys();
+    foreach( const KUrl & prop, keys ) {
+        //kDebug() << "Prop " << prop;
+
+        const QList<Soprano::Node> nodeList = simpleRes.values( prop );
+        res.setProperty( prop, Nepomuk::Variant::fromNodeList( nodeList ) );
+    }
+    return res.resourceUri();
+}
+
+bool Nepomuk::SyncFileIdentifier::runIdentification(const KUrl& uri)
+{
+    if( Nepomuk::Sync::ResourceIdentifier::runIdentification(uri) )
+        return true;
+
+    const Sync::SyncResource res = simpleResource( uri );
+
+    // Add the resource if ( it is NOT a FileDataObject ) or ( if is a FileDataObject and
+    // exists in the filesystem at the nie:url )
+    bool shouldAdd = !res.isFileDataObject();
+    if( res.isFileDataObject() ) {
+        if( QFile::exists( res.nieUrl().toLocalFile() ) )
+            shouldAdd = true;
+    }
+
+    if( shouldAdd ) {
+        Nepomuk::Resource newRes = createNewResource( res );
+        if( newRes.isValid() ) {
+            forceResource( uri, newRes );
+            return true;
+        }
+    }
+
+    return false;
+}
diff --git a/nepomuk/services/backupsync/gui/syncfileidentifier.h b/nepomuk/services/backupsync/gui/syncfileidentifier.h
new file mode 100644
index 0000000..3c1f918
--- /dev/null
+++ b/nepomuk/services/backupsync/gui/syncfileidentifier.h
@@ -0,0 +1,60 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef SYNCFILEIDENTIFIER_H
+#define SYNCFILEIDENTIFIER_H
+
+#include "resourceidentifier.h"
+#include "changelog.h"
+#include "identificationset.h"
+#include "syncfile.h"
+
+namespace Nepomuk {
+
+    class SyncFileIdentifier : public Sync::ResourceIdentifier
+    {
+    public:
+        SyncFileIdentifier( const SyncFile & sf );
+        virtual ~SyncFileIdentifier();
+
+        virtual void identifyAll();
+        int id();
+
+        ChangeLog convertedChangeLog();
+        void load();
+
+    protected:
+        virtual bool runIdentification(const KUrl& uri);
+
+    private:
+        ChangeLog m_changeLog;
+        IdentificationSet m_identificationSet;
+
+        static int NextId;
+        int m_id;
+
+        Resource createNewResource(const Nepomuk::Sync::SyncResource& simpleRes) const;
+
+    };
+}
+
+#endif // SYNCFILEIDENTIFIER_H
diff --git a/nepomuk/services/backupsync/lib/CMakeLists.txt b/nepomuk/services/backupsync/lib/CMakeLists.txt
index 7e1bdf7..2d69956 100644
--- a/nepomuk/services/backupsync/lib/CMakeLists.txt
+++ b/nepomuk/services/backupsync/lib/CMakeLists.txt
@@ -20,14 +20,13 @@ add_definitions(-DDISABLE_NEPOMUK_LEGACY=1)
 set(nepomuksync_SRCS
     resourcemerger.cpp
     resourceidentifier.cpp
-    resourceidentifier_p.cpp
     identificationsetgenerator.cpp
-    simpleresource.cpp
+    syncresource.cpp
 )
 
 soprano_add_ontology(nepomuksync_SRCS
    ${CMAKE_CURRENT_SOURCE_DIR}/../../../ontologies/nrio.trig
-  "backupsync"
+  "NRIO"
   "Nepomuk::Vocabulary"
   "trig")
 
@@ -45,11 +44,11 @@ target_link_libraries(nepomuksync
   )
 
 install(TARGETS nepomuksync EXPORT kdelibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS})
-
-#install(FILES
-#    resourceidentifier.h
-#    resourcemerger.h
-#    simpleresource.h
-#    nepomuksync_export.h
-#    DESTINATION ${INCLUDE_INSTALL_DIR}/nepomuk COMPONENT Devel
-#)
+#
+# install(FILES
+#     resourceidentifier.h
+#     resourcemerger.h
+#     syncresource.h
+#     nepomuksync_export.h
+#     DESTINATION ${INCLUDE_INSTALL_DIR}/nepomuk COMPONENT Devel
+# )
diff --git a/nepomuk/services/backupsync/lib/identificationsetgenerator.cpp b/nepomuk/services/backupsync/lib/identificationsetgenerator.cpp
index d5dd960..ded7478 100644
--- a/nepomuk/services/backupsync/lib/identificationsetgenerator.cpp
+++ b/nepomuk/services/backupsync/lib/identificationsetgenerator.cpp
@@ -20,6 +20,7 @@
 */
 
 #include "identificationsetgenerator_p.h"
+#include "nrio.h"
 
 #include <Soprano/Statement>
 #include <Soprano/Node>
@@ -27,7 +28,6 @@
 #include <Soprano/QueryResultIterator>
 #include <Soprano/Vocabulary/RDF>
 #include <Soprano/Vocabulary/RDFS>
-#include "backupsync.h"
 
 #include <Nepomuk/ResourceManager>
 
@@ -46,7 +46,7 @@ Soprano::QueryResultIterator Nepomuk::Sync::IdentificationSetGenerator::performQ
                                         "UNION { ?p %1 %3. } "
                                         "FILTER( ?r in ( %4 ) ) . } ")
     .arg(Soprano::Node::resourceToN3(Soprano::Vocabulary::RDFS::subPropertyOf()),
-            Soprano::Node::resourceToN3(Nepomuk::Vocabulary::backupsync::identifyingProperty()),
+            Soprano::Node::resourceToN3(Nepomuk::Vocabulary::NRIO::identifyingProperty()),
             Soprano::Node::resourceToN3(Soprano::Vocabulary::RDF::type()),
             uris.join(", "));
 
diff --git a/nepomuk/services/backupsync/lib/nepomuksync_export.h b/nepomuk/services/backupsync/lib/nepomuksync_export.h
index 54e6f4c..efe157c 100644
--- a/nepomuk/services/backupsync/lib/nepomuksync_export.h
+++ b/nepomuk/services/backupsync/lib/nepomuksync_export.h
@@ -40,4 +40,4 @@
 #  define NEPOMUKSYNC_EXPORT_DEPRECATED KDE_DEPRECATED NEPOMUKSYNC_EXPORT
 # endif
 
-#endif
\ No newline at end of file
+#endif
diff --git a/nepomuk/services/backupsync/lib/resourceidentifier.cpp b/nepomuk/services/backupsync/lib/resourceidentifier.cpp
index 51b05db..ee02470 100644
--- a/nepomuk/services/backupsync/lib/resourceidentifier.cpp
+++ b/nepomuk/services/backupsync/lib/resourceidentifier.cpp
@@ -22,8 +22,9 @@
 
 #include "resourceidentifier.h"
 #include "resourceidentifier_p.h"
-#include "simpleresource.h"
+#include "syncresource.h"
 #include "identificationsetgenerator_p.h"
+#include "nrio.h"
 
 #include <QtCore/QSet>
 
@@ -36,23 +37,30 @@
 
 #include <Soprano/Vocabulary/RDF>
 #include <Soprano/Vocabulary/RDFS>
+#include <Soprano/Vocabulary/NAO>
 #include <Nepomuk/Vocabulary/NIE>
-#include "backupsync.h"
 
 #include <Nepomuk/Resource>
 #include <Nepomuk/ResourceManager>
 #include <Nepomuk/Variant>
-#include <Nepomuk/Types/Property>
 
 #include <KDebug>
 #include <KUrl>
 
-Nepomuk::Sync::ResourceIdentifier::ResourceIdentifier(Nepomuk::ResourceManager* rm)
+using namespace Nepomuk::Vocabulary;
+using namespace Soprano::Vocabulary;
+
+Nepomuk::Sync::ResourceIdentifier::ResourceIdentifier(Soprano::Model * model)
     : d( new Nepomuk::Sync::ResourceIdentifier::Private(this) )
 {
-    d->init( rm );
+    d->m_model = model ? model : ResourceManager::instance()->mainModel();
 }
 
+Nepomuk::Sync::ResourceIdentifier::Private::Private( ResourceIdentifier * parent )
+    : q( parent ),
+      m_model(0)
+{
+}
 
 Nepomuk::Sync::ResourceIdentifier::~ResourceIdentifier()
 {
@@ -62,20 +70,21 @@ Nepomuk::Sync::ResourceIdentifier::~ResourceIdentifier()
 
 void Nepomuk::Sync::ResourceIdentifier::addStatement(const Soprano::Statement& st)
 {
-    QHash<KUrl, SimpleResource>::iterator it = d->m_resourceHash.find( st.subject().uri() );
+    SyncResource res;
+    res.setUri( st.subject() );
+
+    QHash<KUrl, SyncResource>::iterator it = d->m_resourceHash.find( res.uri() );
     if( it != d->m_resourceHash.end() ) {
-        SimpleResource & res = it.value();
+        SyncResource & res = it.value();
         res.insert( st.predicate().uri(), st.object() );
         return;
     }
 
-   // Doesn't exist. Create it
-   SimpleResource res;
-   res.setUri( st.subject().uri() );
+   // Doesn't exist - Create it and insert it into the resourceHash
    res.insert( st.predicate().uri(), st.object() );
 
-   d->m_resourceHash.insert( st.subject().uri(), res );
-   d->m_notIdentified.insert( st.subject().uri() );
+   d->m_resourceHash.insert( res.uri(), res );
+   d->m_notIdentified.insert( res.uri() );
 }
 
 void Nepomuk::Sync::ResourceIdentifier::addStatements(const Soprano::Graph& graph)
@@ -84,7 +93,7 @@ void Nepomuk::Sync::ResourceIdentifier::addStatements(const Soprano::Graph& grap
 
     KUrl::List uniqueKeys = resHash.uniqueKeys();
     foreach( const KUrl & resUri, uniqueKeys ) {
-        QHash<KUrl, SimpleResource>::iterator it = d->m_resourceHash.find( resUri );
+        QHash<KUrl, SyncResource>::iterator it = d->m_resourceHash.find( resUri );
         if( it != d->m_resourceHash.end() ) {
             it.value() += resHash.value( resUri );
         }
@@ -103,11 +112,13 @@ void Nepomuk::Sync::ResourceIdentifier::addStatements(const QList< Soprano::Stat
 }
 
 
-void Nepomuk::Sync::ResourceIdentifier::addSimpleResource(const Nepomuk::Sync::SimpleResource& res)
+void Nepomuk::Sync::ResourceIdentifier::addSyncResource(const Nepomuk::Sync::SyncResource& res)
 {
-    QHash<KUrl, SimpleResource>::iterator it = d->m_resourceHash.find( res.uri() );
+    Q_ASSERT( !res.uri().isEmpty() );
+    QHash<KUrl, SyncResource>::iterator it = d->m_resourceHash.find( res.uri() );
     if( it == d->m_resourceHash.end() ) {
         d->m_resourceHash.insert( res.uri(), res );
+        d->m_notIdentified.insert( res.uri() );
     }
     else {
         it.value().unite( res );
@@ -115,19 +126,6 @@ void Nepomuk::Sync::ResourceIdentifier::addSimpleResource(const Nepomuk::Sync::S
 }
 
 
-Nepomuk::ResourceManager* Nepomuk::Sync::ResourceIdentifier::resourceManager() const
-{
-    return d->m_resourceManger;
-}
-
-void Nepomuk::Sync::ResourceIdentifier::setResourceManager(ResourceManager* rm)
-{
-    Q_ASSERT( rm );
-    d->m_resourceManger = rm;
-    d->m_model = rm->mainModel();
-}
-
-
 //
 // Identification
 //
@@ -136,34 +134,153 @@ void Nepomuk::Sync::ResourceIdentifier::identifyAll()
 {
     int totalSize = d->m_notIdentified.size();
     kDebug() << totalSize;
-    
+
     return identify( d->m_notIdentified.toList() );
 }
 
 
 bool Nepomuk::Sync::ResourceIdentifier::identify(const KUrl& uri)
 {
-    identify( KUrl::List() << uri );
-    return d->m_hash.contains( uri );
+    // If already identified
+    if( d->m_hash.contains( uri ) )
+        return true;
+
+    // Avoid recursive calls
+    if( d->m_beingIdentified.contains( uri ) )
+        return false;
+
+    bool result = runIdentification( uri );
+    d->m_beingIdentified.remove( uri );
+
+    if( result )
+        d->m_notIdentified.remove( uri );
+
+    return result;
 }
 
 
 void Nepomuk::Sync::ResourceIdentifier::identify(const KUrl::List& uriList)
 {
     foreach( const KUrl & uri, uriList ) {
-        // If already identified
-        if( d->m_hash.contains( uri ) ) {
+        identify( uri );
+    }
+}
+
+bool Nepomuk::Sync::ResourceIdentifier::runIdentification(const KUrl& uri)
+{
+    const Sync::SyncResource & res = simpleResource( uri );
+
+    // Make sure that the res has some rdf:type statements
+    if( !res.contains( RDF::type() ) ) {
+        kDebug() << "No rdf:type statements - Not identifying";
+        return false;
+    }
+
+    QString query;
+
+    int numIdentifyingProperties = 0;
+    QStringList identifyingProperties;
+
+    QHash< KUrl, Soprano::Node >::const_iterator it = res.constBegin();
+    QHash< KUrl, Soprano::Node >::const_iterator constEnd = res.constEnd();
+    for( ; it != constEnd; it++ ) {
+        const QUrl & prop = it.key();
+
+        // Special handling for rdf:type
+        if( prop == RDF::type() ) {
+            query += QString::fromLatin1(" ?r a %1 . ").arg( it.value().toN3() );
             continue;
         }
-        
-        d->m_beingIdentified.clear();
-        
-        if( d->identify( uri ) ) {
-            d->m_notIdentified.remove( uri );
+
+        if( !isIdentifyingProperty( prop ) ) {
+            continue;
+        }
+
+        identifyingProperties << Soprano::Node::resourceToN3( prop );
+
+        Soprano::Node object = it.value();
+        if( object.isBlank()
+            || ( object.isResource() && object.uri().scheme() == QLatin1String("nepomuk") ) ) {
+
+            QUrl objectUri = object.isResource() ? object.uri() : QString( "_:" + object.identifier() );
+            if( !identify( objectUri ) ) {
+                //kDebug() << "Identification of object " << objectUri << " failed";
+                continue;
+            }
+
+            object = mappedUri( objectUri );
         }
+
+        // FIXME: What about optional properties?
+        query += QString::fromLatin1(" optional { ?r %1 ?o%3 . } . filter(!bound(?o%3) || ?o%3=%2). ")
+                 .arg( Soprano::Node::resourceToN3( prop ),
+                       object.toN3(),
+                       QString::number( numIdentifyingProperties++ ) );
+    }
+
+    if( identifyingProperties.isEmpty() || numIdentifyingProperties == 0 ) {
+        //kDebug() << "No identification properties found!";
+        return false;
+    }
+
+    // Make sure atleast one of the identification properties has been matched
+    // by adding filter( bound(?o1) || bound(?o2) ... )
+    query += QString::fromLatin1("filter( ");
+    for( int i=0; i<numIdentifyingProperties-1; i++ ) {
+        query += QString::fromLatin1(" bound(?o%1) || ").arg( QString::number( i ) );
+    }
+    query += QString::fromLatin1(" bound(?o%1) ) . }").arg( QString::number( numIdentifyingProperties - 1 ) );
+
+    // Construct the entire query
+    QString queryBegin = QString::fromLatin1("select distinct ?r count(?p) as ?cnt "
+    "where { ?r ?p ?o. filter( ?p in (%1) ).")
+    .arg( identifyingProperties.join(",") );
+
+    query = queryBegin + query + QString::fromLatin1(" order by desc(?cnt)");
+
+    kDebug() << query;
+
+    //
+    // Only store the results which have the maximum score
+    //
+    QSet<KUrl> results;
+    int score = -1;
+    Soprano::QueryResultIterator qit = d->m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
+    while( qit.next() ) {
+        //kDebug() << "RESULT: " << qit["r"] << " " << qit["cnt"];
+
+        int count = qit["cnt"].literal().toInt();
+        if( score == -1 ) {
+            score = count;
+        }
+        else if( count < score )
+            break;
+
+        results << qit["r"].uri();
+    }
+
+    //kDebug() << "Got " << results.size() << " results";
+    if( results.empty() )
+        return false;
+
+    KUrl newUri;
+    if( results.size() == 1 )
+        newUri = *results.begin();
+    else {
+        kDebug() << "DUPLICATE RESULTS!";
+        newUri = duplicateMatch( res.uri(), results );
+    }
+
+    if( !newUri.isEmpty() ) {
+        kDebug() << uri << " --> " << newUri;
+        manualIdentification( uri, newUri );
+        return true;
     }
+
+    return false;
 }
 
+
 bool Nepomuk::Sync::ResourceIdentifier::allIdentified() const
 {
     return d->m_notIdentified.isEmpty();
@@ -173,17 +290,22 @@ bool Nepomuk::Sync::ResourceIdentifier::allIdentified() const
 // Getting the info
 //
 
-Nepomuk::Resource Nepomuk::Sync::ResourceIdentifier::mappedResource(const KUrl& resourceUri) const
+Soprano::Model* Nepomuk::Sync::ResourceIdentifier::model()
 {
-    QHash< KUrl, Resource >::iterator it = d->m_hash.find( resourceUri );
-    if( it != d->m_hash.end() )
-        return it.value();
-    return Resource();
+    return d->m_model;
+}
+
+void Nepomuk::Sync::ResourceIdentifier::setModel(Soprano::Model* model)
+{
+    d->m_model = model ? model : ResourceManager::instance()->mainModel();
 }
 
 KUrl Nepomuk::Sync::ResourceIdentifier::mappedUri(const KUrl& resourceUri) const
 {
-    return mappedResource( resourceUri ).resourceUri();
+    QHash< KUrl, KUrl >::iterator it = d->m_hash.find( resourceUri );
+    if( it != d->m_hash.end() )
+        return it.value();
+    return KUrl();
 }
 
 KUrl::List Nepomuk::Sync::ResourceIdentifier::mappedUris() const
@@ -191,19 +313,19 @@ KUrl::List Nepomuk::Sync::ResourceIdentifier::mappedUris() const
     return d->m_hash.uniqueKeys();
 }
 
-QHash< KUrl, Nepomuk::Resource > Nepomuk::Sync::ResourceIdentifier::mappings() const
+QHash<KUrl, KUrl> Nepomuk::Sync::ResourceIdentifier::mappings() const
 {
     return d->m_hash;
 }
 
-Nepomuk::Sync::SimpleResource Nepomuk::Sync::ResourceIdentifier::simpleResource(const KUrl& uri)
+Nepomuk::Sync::SyncResource Nepomuk::Sync::ResourceIdentifier::simpleResource(const KUrl& uri)
 {
-    QHash< KUrl, SimpleResource >::const_iterator it = d->m_resourceHash.constFind( uri );
+    QHash< KUrl, SyncResource >::const_iterator it = d->m_resourceHash.constFind( uri );
     if( it != d->m_resourceHash.constEnd() ) {
         return it.value();
     }
-    
-    return SimpleResource();
+
+    return SyncResource();
 }
 
 
@@ -223,13 +345,19 @@ QSet< KUrl > Nepomuk::Sync::ResourceIdentifier::unidentified() const
     return d->m_notIdentified;
 }
 
+QSet< KUrl > Nepomuk::Sync::ResourceIdentifier::identified() const
+{
+    return d->m_hash.keys().toSet();
+}
+
+
 //
 // Property settings
 //
 
-void Nepomuk::Sync::ResourceIdentifier::addOptionalProperty(const Nepomuk::Types::Property& property)
+void Nepomuk::Sync::ResourceIdentifier::addOptionalProperty(const QUrl& property)
 {
-    d->m_optionalProperties.append( property.uri() );
+    d->m_optionalProperties.append( property );
 }
 
 void Nepomuk::Sync::ResourceIdentifier::clearOptionalProperties()
@@ -242,41 +370,11 @@ KUrl::List Nepomuk::Sync::ResourceIdentifier::optionalProperties() const
     return d->m_optionalProperties;
 }
 
-void Nepomuk::Sync::ResourceIdentifier::addVitalProperty(const Nepomuk::Types::Property& property)
-{
-    d->m_vitalProperties.append( property.uri() );
-}
-
-void Nepomuk::Sync::ResourceIdentifier::clearVitalProperties()
-{
-    d->m_vitalProperties.clear();
-}
-
-KUrl::List Nepomuk::Sync::ResourceIdentifier::vitalProperties() const
-{
-    return d->m_vitalProperties;
-}
-
-
-//
-// Score
-//
-
-float Nepomuk::Sync::ResourceIdentifier::minScore() const
-{
-    return d->m_minScore;
-}
-
-void Nepomuk::Sync::ResourceIdentifier::setMinScore(float score)
-{
-    d->m_minScore = score;
-}
-
 
 namespace {
-    
+
     QString stripFileName( const QString & url ) {
-        kDebug() << url;
+        //kDebug() << url;
         int lastIndex = url.lastIndexOf('/') + 1; // the +1 is because we want to keep the trailing /
         return QString(url).remove( lastIndex, url.size() );
     }
@@ -285,35 +383,35 @@ namespace {
 
 void Nepomuk::Sync::ResourceIdentifier::forceResource(const KUrl& oldUri, const Nepomuk::Resource& res)
 {
-    d->m_hash[ oldUri ] = res;
+    d->m_hash[ oldUri ] = res.resourceUri();
     d->m_notIdentified.remove( oldUri );
 
     if( res.isFile() ) {
         const QUrl nieUrlProp = Nepomuk::Vocabulary::NIE::url();
-        
-        Sync::SimpleResource & simRes = d->m_resourceHash[ oldUri ];
+
+        Sync::SyncResource & simRes = d->m_resourceHash[ oldUri ];
         KUrl oldNieUrl = simRes.nieUrl();
         KUrl newNieUrl = res.property( nieUrlProp ).toUrl();
-        
+
         //
         // Modify resourceUri's nie:url
         //
         simRes.remove( nieUrlProp );
         simRes.insert( nieUrlProp, Soprano::Node( newNieUrl ) );
-        
+
         // Remove from list. Insert later
         d->m_notIdentified.remove( oldUri );
-        
+
         //
         // Modify other non identified resources with similar nie:urls
         //
         QString oldString;
         QString newString;
-        
+
         if( !simRes.isFolder() ) {
             oldString = stripFileName( oldNieUrl.url( KUrl::RemoveTrailingSlash ) );
             newString = stripFileName( newNieUrl.url( KUrl::RemoveTrailingSlash ) );
-            
+
             kDebug() << oldString;
             kDebug() << newString;
         }
@@ -321,18 +419,18 @@ void Nepomuk::Sync::ResourceIdentifier::forceResource(const KUrl& oldUri, const
             oldString = oldNieUrl.url( KUrl::AddTrailingSlash );
             newString = newNieUrl.url( KUrl::AddTrailingSlash );
         }
-        
+
         foreach( const KUrl & uri, d->m_notIdentified ) {
             // Ignore If already identified
             if( d->m_hash.contains( uri ) )
                 continue;
-            
-            Sync::SimpleResource& simpleRes = d->m_resourceHash[ uri ];
+
+            Sync::SyncResource& simpleRes = d->m_resourceHash[ uri ];
             // Check if it has a nie:url
             QString nieUrl = simpleRes.nieUrl().url();
             if( nieUrl.isEmpty() )
                 return;
-            
+
             // Modify the existing nie:url
             if( nieUrl.startsWith(oldString) ) {
                 nieUrl.replace( oldString, newString );
@@ -341,7 +439,7 @@ void Nepomuk::Sync::ResourceIdentifier::forceResource(const KUrl& oldUri, const
                 simpleRes.insert( nieUrlProp, Soprano::Node( KUrl(nieUrl) ) );
             }
         }
-        
+
         d->m_notIdentified.insert( oldUri );
     }
 }
@@ -351,23 +449,23 @@ bool Nepomuk::Sync::ResourceIdentifier::ignore(const KUrl& resUri, bool ignoreSu
 {
     kDebug() << resUri;
     kDebug() << "Ignore Sub : " << ignoreSub;
-    
+
     if( d->m_hash.contains( resUri ) ) {
         kDebug() << d->m_hash;
         return false;
     }
 
     // Remove the resource
-    const Sync::SimpleResource & res = d->m_resourceHash.value( resUri );
+    const Sync::SyncResource & res = d->m_resourceHash.value( resUri );
     d->m_resourceHash.remove( resUri );
     d->m_notIdentified.remove( resUri );
 
     kDebug() << "Removed!";
-    
+
     // Remove all the statements that contain the resoruce
     QList<KUrl> allUris = d->m_resourceHash.uniqueKeys();
     foreach( const KUrl & uri, allUris ) {
-        SimpleResource res = d->m_resourceHash[ uri ];
+        SyncResource res = d->m_resourceHash[ uri ];
         res.removeObject( resUri );
     }
 
@@ -381,16 +479,16 @@ bool Nepomuk::Sync::ResourceIdentifier::ignore(const KUrl& resUri, bool ignoreSu
     QList<Soprano::Node> nieUrlNodes = res.values( nieUrlProp );
     if( nieUrlNodes.size() != 1 )
         return false;
-    
+
     KUrl mainNieUrl = nieUrlNodes.first().uri();
-    
+
     foreach( const KUrl & uri, d->m_notIdentified ) {
-        Sync::SimpleResource res = d->m_resourceHash[ uri ];
-        
+        Sync::SyncResource res = d->m_resourceHash[ uri ];
+
         // If already identified
         if( d->m_hash.contains(uri) )
             continue;
-        
+
         // Check if it has a nie:url
         QList<Soprano::Node> nieUrls = res.values( nieUrlProp );
         if( nieUrls.empty() )
@@ -408,12 +506,11 @@ bool Nepomuk::Sync::ResourceIdentifier::ignore(const KUrl& resUri, bool ignoreSu
 }
 
 
-KUrl Nepomuk::Sync::ResourceIdentifier::duplicateMatch(const KUrl& uri, const QSet< KUrl >& matchedUris, float score)
+KUrl Nepomuk::Sync::ResourceIdentifier::duplicateMatch(const KUrl& uri, const QSet< KUrl >& matchedUris)
 {
     Q_UNUSED( uri );
     Q_UNUSED( matchedUris );
-    Q_UNUSED( score );
-    
+
     // By default - Identification fails
     return KUrl();
 }
@@ -425,9 +522,29 @@ Soprano::Graph Nepomuk::Sync::ResourceIdentifier::createIdentifyingStatements(co
     return gen.generate();
 }
 
-Nepomuk::Resource Nepomuk::Sync::ResourceIdentifier::additionalIdentification(const KUrl& uri)
+void Nepomuk::Sync::ResourceIdentifier::manualIdentification(const KUrl& oldUri, const KUrl& newUri)
 {
-    Q_UNUSED( uri );
-    // Do nothing - identification fails
-    return Nepomuk::Resource();
+    d->m_hash[ oldUri ] = newUri;
+    d->m_notIdentified.remove( oldUri );
+}
+
+bool Nepomuk::Sync::ResourceIdentifier::isIdentifyingProperty(const QUrl& uri)
+{
+    if( uri == NAO::created()
+        || uri == NAO::creator()
+        || uri == NAO::lastModified()
+        || uri == NAO::userVisible() ) {
+        return false;
+    }
+
+    // TODO: Hanlde nxx:FluxProperty and nxx:resourceRangePropWhichCanIdentified
+    const QString query = QString::fromLatin1("ask { %1 %2 ?range . "
+                                                " %1 a %3 . "
+                                                "{ FILTER( regex(str(?range), '^http://www.w3.org/2001/XMLSchema#') ) . }"
+                                                " UNION { %1 a rdf:Property . } }") // rdf:Property should be nxx:resourceRangePropWhichCanIdentified
+                            .arg( Soprano::Node::resourceToN3( uri ),
+                                Soprano::Node::resourceToN3( RDFS::range() ),
+                                Soprano::Node::resourceToN3( RDF::Property() ) );
+
+    return model()->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue();
 }
diff --git a/nepomuk/services/backupsync/lib/resourceidentifier.h b/nepomuk/services/backupsync/lib/resourceidentifier.h
index ab43f19..2a787c4 100644
--- a/nepomuk/services/backupsync/lib/resourceidentifier.h
+++ b/nepomuk/services/backupsync/lib/resourceidentifier.h
@@ -31,20 +31,16 @@
 namespace Soprano {
     class Statement;
     class Graph;
+    class Model;
 }
 
 namespace Nepomuk {
 
     class Resource;
-    class ResourceManager;
 
-    namespace Types {
-        class Property;
-    }
-    
     namespace Sync {
 
-        class SimpleResource;
+        class SyncResource;
 
         /**
          * \class ResourceIdentifier resourceidentifier.h
@@ -52,30 +48,30 @@ namespace Nepomuk {
          * This class is used to identify already existing resources from a set of
          * properties and objects. It identifies the resources on the basis of the
          * identifying statements provided.
-         * 
+         *
          * \author Vishesh Handa <handa.vish@gmail.com>
          */
         class NEPOMUKSYNC_EXPORT ResourceIdentifier
         {
         public:
-            ResourceIdentifier( ResourceManager * rm = 0 );
+            ResourceIdentifier( Soprano::Model * model = 0 );
             virtual ~ResourceIdentifier();
 
-            ResourceManager * resourceManager() const;
-            void setResourceManager( ResourceManager * rm );
+            Soprano::Model * model();
+            void setModel( Soprano::Model * model );
 
             //
             // Processing
             //
-            virtual void identifyAll();
+            void identifyAll();
 
-            virtual bool identify( const KUrl & uri );
+            bool identify( const KUrl & uri );
 
             /**
              * Identifies all the resources present in the \p uriList.
              */
-            virtual void identify( const KUrl::List & uriList );
-            
+            void identify( const KUrl::List & uriList );
+
             /**
              * This returns true if ALL the external ResourceUris have been identified.
              * If this is false, you should manually identify some of the resources by
@@ -88,31 +84,31 @@ namespace Nepomuk {
             virtual void addStatement( const Soprano::Statement & st );
             virtual void addStatements( const Soprano::Graph& graph );
             virtual void addStatements( const QList<Soprano::Statement> & stList );
-            virtual void addSimpleResource( const SimpleResource & res );
-            
+            virtual void addSyncResource( const SyncResource & res );
+
             //
             // Getting the info
             //
             /**
              * Returns the detected uri for the given resourceUri.
-             * This method usefull only after identifyAll() method was called
+             * This method useful only after identifyAll() method was called
              */
             KUrl mappedUri( const KUrl & resourceUri ) const;
 
-            Resource mappedResource( const KUrl & resourceUri ) const;
-
             KUrl::List mappedUris() const;
-            
+
             /**
              * Returns mappings of the identified uri
              */
-            QHash<KUrl, Resource> mappings() const;
+            QHash<KUrl, KUrl> mappings() const;
 
             /**
              * Returns urls that were not successfully identified
              */
             QSet<KUrl> unidentified() const;
 
+            QSet<KUrl> identified() const;
+
             /**
              * Returns all the statements that are being used to identify \p uri
              */
@@ -120,20 +116,7 @@ namespace Nepomuk {
 
             QList<Soprano::Statement> identifyingStatements() const;
 
-            SimpleResource simpleResource( const KUrl & uri );
-            //
-            // Score
-            //
-            /**
-             * Returns the min % of the number of statements that should match during identification
-             * in order for a resource to be successfully identified.
-             *
-             * Returns a value between [0,1]
-             */
-            float minScore() const;
-
-            void setMinScore( float score );
-
+            SyncResource simpleResource( const KUrl & uri );
             //
             // Property Settings
             //
@@ -141,23 +124,11 @@ namespace Nepomuk {
              * The property \p prop will be matched during identification, but it will
              * not contribute to the actual score if it cannot be matched.
              */
-            void addOptionalProperty( const Types::Property & property );
+            void addOptionalProperty( const QUrl & property );
 
             void clearOptionalProperties();
-            
-            KUrl::List optionalProperties() const;
-
-            /**
-            * If the property \p prop cannot be matched during identification then the
-            * identification for that resource will fail.
-            *
-            * By default - rdf:type is the only vital property
-            */
-            void addVitalProperty( const Types::Property & property );
 
-            void clearVitalProperties();
-            
-            KUrl::List vitalProperties() const;
+            KUrl::List optionalProperties() const;
 
             //
             // Manual Identification
@@ -186,12 +157,13 @@ namespace Nepomuk {
              * contain \p uri as the object.
              */
             bool ignore( const KUrl& uri, bool ignoreSub = false );
-            
+
             //
             // Identification Statement generator
             //
             static Soprano::Graph createIdentifyingStatements( const KUrl::List & uriList );
 
+            virtual bool isIdentifyingProperty( const QUrl & uri );
         private:
             class Private;
             Private * d;
@@ -202,13 +174,20 @@ namespace Nepomuk {
             *
             * The default behavior is to return an empty uri, which depicts identification failure
             */
-            virtual KUrl duplicateMatch( const KUrl & uri, const QSet<KUrl> & matchedUris, float score );
+            virtual KUrl duplicateMatch( const KUrl & uri, const QSet<KUrl> & matchedUris );
+
+            /**
+             * This function returns true if identification was successful, and false if it was not.
+             * If you need to customize the identification process, you will need to overload this
+             * function.
+             */
+            virtual bool runIdentification( const KUrl& uri );
 
             /**
-             * In case identification fails for \p uri this method would be called. Derived classes
-             * can implement their own identification mechanisms over here.
+             * Sets oldUri -> newUri in the mappings.
+             * This is useful when runIdentification has been reimplemented.
              */
-            virtual Nepomuk::Resource additionalIdentification( const KUrl & uri );
+            void manualIdentification( const KUrl & oldUri, const KUrl & newUri );
         };
     }
 }
diff --git a/nepomuk/services/backupsync/lib/resourceidentifier_p.cpp b/nepomuk/services/backupsync/lib/resourceidentifier_p.cpp
deleted file mode 100644
index a53fab6..0000000
--- a/nepomuk/services/backupsync/lib/resourceidentifier_p.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "resourceidentifier.h"
-#include "resourceidentifier_p.h"
-#include "backupsync.h"
-
-#include <QtCore/QDir>
-#include <QtCore/QSet>
-
-#include <Soprano/Statement>
-#include <Soprano/Vocabulary/RDF>
-#include <Soprano/Vocabulary/RDFS>
-#include <Soprano/Model>
-#include <Soprano/QueryResultIterator>
-#include <Soprano/StatementIterator>
-#include <Soprano/NodeIterator>
-#include <Soprano/Node>
-
-#include <Nepomuk/ResourceManager>
-#include <Nepomuk/Resource>
-#include <Nepomuk/Variant>
-
-#include <Nepomuk/Vocabulary/NIE>
-#include <Nepomuk/Vocabulary/NFO>
-#include <Soprano/Vocabulary/NAO>
-
-#include <KDebug>
-#include <KUrl>
-
-Nepomuk::Sync::ResourceIdentifier::Private::Private( ResourceIdentifier * parent )
-    : q( parent ),
-      m_model(0),
-      m_resourceManger(0),
-      m_minScore( 0.60 )
-{;}
-
-
-void Nepomuk::Sync::ResourceIdentifier::Private::init(Nepomuk::ResourceManager* rm)
-{
-    if( !rm )
-        m_resourceManger = ResourceManager::instance();
-    m_model = m_resourceManger->mainModel();
-
-    // rdf:type is by default vital
-    m_vitalProperties.append( Soprano::Vocabulary::RDF::type() );
-}
-
-
-namespace {
-
-    //TODO: Use Nepomuk::Type::Property
-    bool isIdentifyingProperty( KUrl prop, Soprano::Model * model ) {
-        QString query = QString::fromLatin1( "ask { %1 %2 %3 }" )
-        .arg( Soprano::Node::resourceToN3( prop ) )
-        .arg( Soprano::Node::resourceToN3(Soprano::Vocabulary::RDFS::subPropertyOf()) )
-        .arg( Soprano::Node::resourceToN3(Nepomuk::Vocabulary::backupsync::identifyingProperty()) );
-        return model->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue();
-    }
-}
-
-
-
-bool Nepomuk::Sync::ResourceIdentifier::Private::identify( const KUrl& oldUri )
-{
-    kDebug() << oldUri;
-    
-    if( m_hash.contains( oldUri ) )
-        return true;
-
-    const SimpleResource & res = m_resourceHash[ oldUri ];
-    KUrl resourceUri = findMatch( res );
-    
-    if( resourceUri.isEmpty() ) {
-        resourceUri = q->additionalIdentification( oldUri ).resourceUri();
-        if( resourceUri.isEmpty() )
-            return false;
-    }
-    m_hash[ oldUri ] = resourceUri;
-    
-    kDebug() << oldUri << " ---> " << resourceUri;
-    return true;
-}
-
-
-
-bool Nepomuk::Sync::ResourceIdentifier::Private::queryIdentify(const KUrl& oldUri)
-{
-    if( m_beingIdentified.contains( oldUri ) )
-        return false;
-    bool result = identify( oldUri );
-
-    if( result )
-        m_notIdentified.remove( oldUri );
-
-    return result;
-}
-
-
-//TODO: Optimize
-KUrl Nepomuk::Sync::ResourceIdentifier::Private::findMatch(const Nepomuk::Sync::SimpleResource& simpleRes)
-{
-    kDebug() << "SimpleResource: " << simpleRes;
-    //
-    // Vital Properties
-    //
-    int numOfVitalStatements = 0;
-    QString query = QString::fromLatin1("select distinct ?r where {");
-    kDebug() << m_vitalProperties;
-    foreach( const KUrl & prop, m_vitalProperties ) {
-        QList<Soprano::Node> objects = simpleRes.property( prop );
-        foreach( const Soprano::Node & obj, objects ) {
-            query += QString::fromLatin1(" ?r %1 %2. ")
-                     .arg( Soprano::Node::resourceToN3( prop ),
-                           obj.toN3() );
-            numOfVitalStatements++;
-        }
-    }
-    query += " }";
-
-    kDebug() << "Number of Vital Statements : " << numOfVitalStatements;
-    kDebug() << query;
-    //
-    // Insert them in resourceCount with count = 0
-    //
-    Soprano::QueryResultIterator it = m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
-
-    // The first int is the score while the second int is the additional
-    // maxScore ( for optional properties )
-    QHash<KUrl, QPair<int, int> > resourceCount;
-    while( it.next() ) {
-        resourceCount.insert( it[0].uri(), QPair<int, int>(0,0) );
-    }
-
-    // No match
-    if( resourceCount.isEmpty() )
-        return KUrl();
-    
-
-    //
-    // Get all the other properties, and increase resourceCount accordingly.
-    // Ignore vital properties. Don't increment the maxScore when an optional property is not found. 
-    //
-    int numStatementsCompared = 0;
-    QList<KUrl> properties = simpleRes.uniqueKeys();
-    foreach( const KUrl & propUri, properties ) {
-
-        if( m_vitalProperties.contains( propUri ) )
-            continue;
-
-        bool isOptionalProp = m_optionalProperties.contains( propUri );
-        
-        Soprano::Statement statement( Soprano::Node(), propUri, Soprano::Node(), Soprano::Node() );
-
-        QList<Soprano::Node> objList = simpleRes.values( propUri );
-        foreach( const Soprano::Node& n, objList ) {
-            if( n.isResource() && n.uri().scheme() == QLatin1String("nepomuk") ) {
-                if( !queryIdentify( n.uri() ) ) {
-                    continue;
-                }
-            }
-            statement.setObject( n );
-
-            Soprano::NodeIterator iter = m_model->listStatements( statement ).iterateSubjects();
-            while( iter.next() ) {
-                QHash< KUrl, QPair<int,int> >::iterator it = resourceCount.find( iter.current().uri() );
-
-                if( it != resourceCount.end() ) {
-                    if( isOptionalProp ) {
-                        // It is an optional property and it has matched
-                        // -> Increase the score ( first )
-                        // -> and increase the max score ( second )
-                        // ( optional properties don't contribute to the max score unless matched )
-                        it.value().first++;
-                        it.value().second++;
-                    }
-                    else {
-                        if( it != resourceCount.end() ) {
-                            it.value().first++; // only increase the score
-                        }
-                    }
-                }
-            }
-            if( !isOptionalProp )
-                numStatementsCompared++;
-        }
-    }
-    
-    //
-    // Find the resources with the max score
-    //
-    QSet<KUrl> maxResources;
-    float maxScore = -1;
-    
-    foreach( const KUrl & key, resourceCount.keys() ) {
-        QPair<int, int> scorePair = resourceCount.value( key );
-
-        // The divisor will be the total number of statements it was compared to
-        // ie optional-properties-matched ( stored in scorePair.second ) + numStatementsCompared
-        float divisor = scorePair.second + numStatementsCompared;
-        float score = 0;
-        if( divisor )
-            score = scorePair.first / divisor;
-        
-        if( score > maxScore ) {
-            maxScore = score;
-            maxResources.clear();
-            maxResources.insert( key );
-        }
-        else if( score == maxScore ) {
-            maxResources.insert( key );
-        }
-    }
-    
-    if( maxScore < m_minScore ) {
-        return KUrl();
-    }
-
-    if( maxResources.empty() )
-        return KUrl();
-
-    if( maxResources.size() > 1 ) {
-        kDebug() << "WE GOT A PROBLEM!!";
-        kDebug() << "More than one resource with the exact same score found";
-        kDebug() << "NOT IDENTIFYING IT! Do it manually!";
-
-        return q->duplicateMatch( simpleRes.uri(), maxResources, maxScore );
-    }
-
-    return (*maxResources.begin());
-}
-
diff --git a/nepomuk/services/backupsync/lib/resourceidentifier_p.h b/nepomuk/services/backupsync/lib/resourceidentifier_p.h
index a9543ff..7c98603 100644
--- a/nepomuk/services/backupsync/lib/resourceidentifier_p.h
+++ b/nepomuk/services/backupsync/lib/resourceidentifier_p.h
@@ -31,7 +31,7 @@
 #include <KUrl>
 
 #include "resourceidentifier.h"
-#include "simpleresource.h"
+#include "syncresource.h"
 #include <Nepomuk/ResourceManager>
 
 
@@ -43,75 +43,43 @@ namespace Soprano {
 
 namespace Nepomuk {
     namespace Sync {
-        
+
         class ResourceIdentifier::Private
         {
         public:
             /**
              * Contstructor.
              * It will initialize all pointers with NULL and all values that
-             * has a incorrect value with this value. For example -1 will be
-             * assigned for m_id.
+             * has a incorrect value with this value.
              */
-            Private( ResourceIdentifier * parent );
-            void init( ResourceManager * rm );
-            
+            Private( Nepomuk::Sync::ResourceIdentifier* parent );
+
             ResourceIdentifier * q;
-            
+
             Soprano::Model * m_model;
-            ResourceManager * m_resourceManger;
-            
+
             /**
              * The main identification hash which maps external ResourceUris
              * with the internal ones
              */
-            QHash<KUrl, Nepomuk::Resource> m_hash;
-            
+            QHash<KUrl, KUrl> m_hash;
+
             QSet<KUrl> m_notIdentified;
-            
+
             /// Used to store all the identification statements
             ResourceHash m_resourceHash;
 
             //
             // Properties
             //
-            KUrl::List m_vitalProperties;
             KUrl::List m_optionalProperties;
-            
+
             /**
              * This contains all the urls that are being identified, at any moment.
              * It is used to avoid infinite recursion while generating the sparql
              * query.
              */
             QSet<KUrl> m_beingIdentified;
-            
-            float m_minScore;
-            
-            //
-            // Identification Procedures
-            //
-            
-
-            bool identify( const KUrl & uri );
-            
-            /**
-             * Checks if the @p oldUri is already in the process of being identified.
-             * The function returns false if it is being identified otherwise it
-             * returns the value of identify( const KUrl & )
-             *
-             * \sa identify
-             */
-            bool queryIdentify( const KUrl & oldUri );
-            
-            /**
-             * Finds the best possible match for \p rs from the internal model. It uses
-             * constructIdentificationQuery to create the query
-             *
-             * \param minScore dictates the min number of properties that should match
-             *
-             * \sa queryIdentify
-             */
-            KUrl findMatch( const Nepomuk::Sync::SimpleResource& simpleRes );
         };
     }
 }
diff --git a/nepomuk/services/backupsync/lib/resourcemerger.cpp b/nepomuk/services/backupsync/lib/resourcemerger.cpp
index c84263e..613904f 100644
--- a/nepomuk/services/backupsync/lib/resourcemerger.cpp
+++ b/nepomuk/services/backupsync/lib/resourcemerger.cpp
@@ -1,6 +1,6 @@
 /*
     This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+    Copyright (C) 2010-11  Vishesh Handa <handa.vish@gmail.com>
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
@@ -22,36 +22,35 @@
 
 #include "resourcemerger.h"
 
-#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
-
 #include <Soprano/Model>
-#include <Soprano/Vocabulary/NRL>
-#include <Soprano/NRLModel>
 #include <Soprano/Graph>
 
-#include <Nepomuk/Resource>
+#include <Soprano/Vocabulary/NRL>
+#include <Soprano/Vocabulary/RDF>
+
 #include <Nepomuk/ResourceManager>
 
 #include <KUrl>
 #include <KDebug>
 
+using namespace Soprano::Vocabulary;
+
 class Nepomuk::Sync::ResourceMerger::Private {
 public:
     Private( ResourceMerger * resMerger );
-    
+
     Soprano::Model * m_model;
-    ResourceManager * m_resourceManager;
 
-    Soprano::NRLModel * m_nrlModel;
-    KUrl m_graphType;
+    KUrl m_graph;
 
     ResourceMerger * q;
-    
-    QHash<KUrl, Resource> m_oldMappings;
-    QHash<KUrl, Resource> m_newMappings;
 
-    void push( const Soprano::Statement & st, const KUrl & graphUri );
-    KUrl resolve( const KUrl& oldUri );
+    QHash<KUrl, KUrl> m_mappings;
+
+    QMultiHash<QUrl, Soprano::Node> m_additionalMetadata;
+
+    bool push( const Soprano::Statement & st );
+    KUrl resolve( const Soprano::Node & n );
 };
 
 Nepomuk::Sync::ResourceMerger::Private::Private(Nepomuk::Sync::ResourceMerger* resMerger)
@@ -60,12 +59,11 @@ Nepomuk::Sync::ResourceMerger::Private::Private(Nepomuk::Sync::ResourceMerger* r
 }
 
 
-Nepomuk::Sync::ResourceMerger::ResourceMerger(Nepomuk::ResourceManager* rm)
+Nepomuk::Sync::ResourceMerger::ResourceMerger( Soprano::Model * model, const QHash<KUrl, KUrl> & mappings )
     : d( new Nepomuk::Sync::ResourceMerger::Private( this ) )
 {
-    d->m_nrlModel = 0;
-    setResourceManager( rm );
-    d->m_graphType = Soprano::Vocabulary::NRL::InstanceBase();
+    setModel( model );
+    setMappings( mappings );
 }
 
 Nepomuk::Sync::ResourceMerger::~ResourceMerger()
@@ -73,117 +71,196 @@ Nepomuk::Sync::ResourceMerger::~ResourceMerger()
     delete d;
 }
 
-void Nepomuk::Sync::ResourceMerger::setResourceManager(Nepomuk::ResourceManager* rm)
+void Nepomuk::Sync::ResourceMerger::setModel(Soprano::Model* model)
 {
-    if( !rm )
-        d->m_resourceManager = ResourceManager::instance();
-    d->m_model = d->m_resourceManager->mainModel();
-    
-    delete d->m_nrlModel;
-    d->m_nrlModel = new Soprano::NRLModel( d->m_model );
+    if( model == 0 ) {
+        d->m_model = ResourceManager::instance()->mainModel();
+        return;
+    }
+    d->m_model = model;
 }
 
-Nepomuk::ResourceManager* Nepomuk::Sync::ResourceMerger::resourceManager() const
+Soprano::Model* Nepomuk::Sync::ResourceMerger::model() const
 {
-    return d->m_resourceManager;
+    return d->m_model;
 }
 
+void Nepomuk::Sync::ResourceMerger::setMappings(const QHash< KUrl, KUrl >& mappings)
+{
+    d->m_mappings = mappings;
+}
 
-Soprano::Model* Nepomuk::Sync::ResourceMerger::model() const
+QHash< KUrl, KUrl > Nepomuk::Sync::ResourceMerger::mappings() const
 {
-    return d->m_resourceManager->mainModel();
+    return d->m_mappings;
 }
 
+void Nepomuk::Sync::ResourceMerger::setAdditionalGraphMetadata(const QMultiHash< QUrl, Soprano::Node >& additionalMetadata)
+{
+    d->m_additionalMetadata = additionalMetadata;
+}
 
-Nepomuk::Types::Class Nepomuk::Sync::ResourceMerger::graphType() const
+QMultiHash< QUrl, Soprano::Node > Nepomuk::Sync::ResourceMerger::additionalMetadata() const
 {
-    return d->m_graphType;
+    return d->m_additionalMetadata;
 }
 
-bool Nepomuk::Sync::ResourceMerger::setGraphType(const Nepomuk::Types::Class& type)
+KUrl Nepomuk::Sync::ResourceMerger::resolveUnidentifiedResource(const KUrl& uri)
 {
-    if( type.isSubClassOf( Soprano::Vocabulary::NRL::Graph() ) ){
-        d->m_graphType = type.uri();
-        return true;
-    }
-    return false;
+    //TODO: The resource should also get its metadata -> nao:created, nao:lastModified
+    KUrl newUri = createResourceUri();
+    d->m_mappings.insert( uri, newUri );
+    return newUri;
 }
 
 
-Nepomuk::Resource Nepomuk::Sync::ResourceMerger::resolveUnidentifiedResource(const KUrl& uri)
+bool Nepomuk::Sync::ResourceMerger::merge( const Soprano::Graph& graph )
 {
-    // The default implementation is to create it.
-    QHash< KUrl, Resource >::const_iterator it = d->m_newMappings.constFind( uri );
-    if( it != d->m_newMappings.constEnd() )
-        return it.value();
-    
-    KUrl newUri = d->m_resourceManager->generateUniqueUri( QString("res") );
-    d->m_newMappings.insert( uri, newUri );
-    return Nepomuk::Resource( newUri );
+    const QList<Soprano::Statement> statements = graph.toList();
+    foreach( Soprano::Statement st, statements ) {
+        if(!mergeStatement( st ))
+            return false;
+    }
+    return true;
 }
 
 
-void Nepomuk::Sync::ResourceMerger::merge(const Soprano::Graph& graph, const QHash< KUrl, Nepomuk::Resource >& mappings)
+bool Nepomuk::Sync::ResourceMerger::resolveStatement(Soprano::Statement& st)
 {
-    d->m_oldMappings = mappings;
+    if( !st.isValid() ) {
+        QString error = QString::fromLatin1("Invalid statement encountered");
+        return false;
+    }
 
-    KUrl graphUri = createGraph();
-    
-    QList<Soprano::Statement> statements = graph.toList();
-    foreach( Soprano::Statement st, statements ) {
-        if( !st.isValid() )
-            continue;
-
-        st.setSubject( d->resolve( st.subject().uri() ) );
-        if( st.object().isResource() ) {
-            KUrl resolvedObject = d->resolve( st.object().uri() );
-            if( resolvedObject.isEmpty() ) {
-                kDebug() << st.object().uri() << " resolution failed!";
-                continue;
-            }
-            st.setObject( resolvedObject );
-        }
+    KUrl resolvedSubject = d->resolve( st.subject() );
+    if( !resolvedSubject.isValid() ) {
+        QString error = QString::fromLatin1("Subject - %1 resolution failed")
+                        .arg( st.subject().toN3() );
+        kDebug() << error;
+        setError( error );
+        return false;
+    }
 
-        d->push( st, graphUri );
+    st.setSubject( resolvedSubject );
+    Soprano::Node object = st.object();
+    if( (object.isResource() && object.uri().scheme() == QLatin1String("nepomuk") )
+        || object.isBlank() ) {
+        KUrl resolvedObject = d->resolve( object );
+        if( resolvedObject.isEmpty() ) {
+            QString error = QString::fromLatin1("Object - %1 resolution failed")
+                            .arg( object.toN3() );
+            kDebug() << error;
+            setError( error );
+            return false;
+        }
+        st.setObject( resolvedObject );
     }
+
+    return true;
+}
+
+
+bool Nepomuk::Sync::ResourceMerger::mergeStatement(const Soprano::Statement& statement)
+{
+    Soprano::Statement st( statement );
+    if( !resolveStatement( st ) )
+        return false;
+
+    return d->push( st );
 }
 
 
 KUrl Nepomuk::Sync::ResourceMerger::createGraph()
 {
-    return d->m_nrlModel->createGraph( d->m_graphType );
+    KUrl graphUri = createGraphUri();
+    KUrl metadataGraph = createResourceUri();
+
+    addStatement( metadataGraph, RDF::type(), NRL::GraphMetadata(), metadataGraph );
+    addStatement( metadataGraph, NRL::coreGraphMetadataFor(), graphUri, metadataGraph );
+
+    if( !d->m_additionalMetadata.contains( RDF::type(), NRL::InstanceBase() ) )
+        d->m_additionalMetadata.insert( RDF::type(), NRL::InstanceBase() );
+
+    for(QHash<QUrl, Soprano::Node>::const_iterator it = d->m_additionalMetadata.constBegin();
+        it != d->m_additionalMetadata.constEnd(); ++it) {
+        addStatement(graphUri, it.key(), it.value(), metadataGraph);
+    }
+
+    return graphUri;
+}
+
+
+KUrl Nepomuk::Sync::ResourceMerger::graph()
+{
+    if( !d->m_graph.isValid() ) {
+        d->m_graph = createGraph();
+        if( !d->m_graph.isValid() ) {
+            setError( QString::fromLatin1("Graph creation failed. A valid graph was not returned %1")
+                      .arg( d->m_graph.url() ), Soprano::Error::ErrorInvalidArgument );
+        }
+    }
+    return d->m_graph;
 }
 
 
-void Nepomuk::Sync::ResourceMerger::Private::push(const Soprano::Statement& st, const KUrl& graphUri)
+bool Nepomuk::Sync::ResourceMerger::Private::push(const Soprano::Statement& st)
 {
+    Soprano::Statement statement( st );
     if( m_model->containsAnyStatement( st.subject(), st.predicate(), st.object() ) ) {
-        // Already exists. Ignore
-        return;
+        return q->resolveDuplicate( statement );
     }
 
-    Soprano::Statement statement( st );
-    if( statement.context().isEmpty() )
-        statement.setContext( graphUri );
-    
-    m_model->addStatement( statement );
+    if( !m_graph.isValid() ) {
+        m_graph = q->createGraph();
+        if( !m_graph.isValid() )
+            return false;
+    }
+    statement.setContext( m_graph );
+    //kDebug() << "Pushing - " << statement;
+    return q->addStatement( statement ) == Soprano::Error::ErrorNone;
 }
 
 
-KUrl Nepomuk::Sync::ResourceMerger::Private::resolve(const KUrl& oldUri)
+KUrl Nepomuk::Sync::ResourceMerger::Private::resolve(const Soprano::Node& n)
 {
+    const QUrl oldUri = n.isResource() ? n.uri() : QUrl( n.toN3() );
+
     // Find in mappings
-    QHash< KUrl, Resource >::const_iterator it = m_oldMappings.constFind( oldUri );
-    if( it != m_oldMappings.constEnd() ) {
-        return it.value().resourceUri();
+    QHash< KUrl, KUrl >::const_iterator it = m_mappings.constFind( oldUri );
+    if( it != m_mappings.constEnd() ) {
+        return it.value();
     } else {
-        Nepomuk::Resource res = q->resolveUnidentifiedResource( oldUri );
-        return res.resourceUri();
+        return q->resolveUnidentifiedResource( oldUri );
     }
 }
 
-void Nepomuk::Sync::ResourceMerger::push(const Soprano::Statement& st)
+bool Nepomuk::Sync::ResourceMerger::resolveDuplicate(const Soprano::Statement& /*newSt*/)
 {
-    if( !st.context().isEmpty() )
-        d->push( st, QUrl() );
+    return true;
 }
+
+bool Nepomuk::Sync::ResourceMerger::push(const Soprano::Statement& st)
+{
+    return d->push( st );
+}
+
+QUrl Nepomuk::Sync::ResourceMerger::createResourceUri()
+{
+    return ResourceManager::instance()->generateUniqueUri("res");
+}
+
+QUrl Nepomuk::Sync::ResourceMerger::createGraphUri()
+{
+    return ResourceManager::instance()->generateUniqueUri("ctx");
+}
+
+Soprano::Error::ErrorCode Nepomuk::Sync::ResourceMerger::addStatement(const Soprano::Statement& st)
+{
+    return d->m_model->addStatement( st );
+}
+
+Soprano::Error::ErrorCode Nepomuk::Sync::ResourceMerger::addStatement(const Soprano::Node& subject, const Soprano::Node& property, const Soprano::Node& object, const Soprano::Node& graph)
+{
+    return d->m_model->addStatement( Soprano::Statement(subject, property, object, graph) );
+}
+
diff --git a/nepomuk/services/backupsync/lib/resourcemerger.h b/nepomuk/services/backupsync/lib/resourcemerger.h
index bbb3d70..03ec014 100644
--- a/nepomuk/services/backupsync/lib/resourcemerger.h
+++ b/nepomuk/services/backupsync/lib/resourcemerger.h
@@ -1,6 +1,6 @@
 /*
     This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+    Copyright (C) 2010-11  Vishesh Handa <handa.vish@gmail.com>
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
@@ -29,11 +29,13 @@
 #include <KUrl>
 
 #include "nepomuksync_export.h"
+#include <Soprano/Error/ErrorCode>
 
 namespace Soprano {
     class Statement;
     class Model;
     class Graph;
+    class Node;
 }
 
 namespace Nepomuk {
@@ -44,7 +46,7 @@ namespace Nepomuk {
     namespace Types {
         class Class;
     }
-    
+
     namespace Sync {
 
         /**
@@ -56,59 +58,132 @@ namespace Nepomuk {
          *
          * By default, it pushes all the statements with a nrl:InstanceBase graph. If a
          * statement already exists in the repository then it is NOT overwritten.
-         * 
+         *
          * \author Vishesh Handa <handa.vish@gmail.com>
          */
-        class NEPOMUKSYNC_EXPORT ResourceMerger
+        class NEPOMUKSYNC_EXPORT ResourceMerger : public Soprano::Error::ErrorCache
         {
         public:
-            ResourceMerger( ResourceManager * rm = 0 );
+            ResourceMerger( Soprano::Model * model =0, const QHash<KUrl, KUrl> & mappings = (QHash<KUrl, KUrl>()) );
             virtual ~ResourceMerger();
 
-            ResourceManager * resourceManager() const;
-            void setResourceManager( ResourceManager * rm );
-
+            void setModel( Soprano::Model * model );
             Soprano::Model * model() const;
 
+            void setMappings( const QHash<KUrl, KUrl> & mappings );
+            QHash<KUrl, KUrl> mappings() const;
+
+            /**
+             * Merges all the statements in \p graph into the model, by calling
+             * mergeStatement
+             *
+             * It stops merging if any of the statements in \p graph fail to merge
+             *
+             * \sa mergeStatement
+             * \return \c true if merging was successful
+             */
+            virtual bool merge( const Soprano::Graph & graph );
+
             /**
-             * Pushes all the statements in \p graph into the model. If the statement
+             * Merges the statement \p st into the model. If the statement
              * already exists then resolveDuplicate() is called.
              *
              * If any of the statements contains a graph, then that graph is used. Otherwise
-             * a newly generated graph is used.
+             * createGraph() is called which returns a new graph.
+             *
+             * lastError() is set, if merging fails.
              *
-             * \sa setGraphType graphType
+             * \sa createGraph
+             * \return \c true if the merging was sucessful.
+             *         \c false if merging failed
              */
-            virtual void merge( const Soprano::Graph & graph, const QHash<KUrl, Resource> & mappings );
+            virtual bool mergeStatement( const Soprano::Statement & st );
 
             /**
-             * The graph type by default is nrl:InstanceBase. If \p type is not a subclass of
-             * nrl:Graph then it is ignored.
+             * Sets the graph metadata which will be used to create a graph.
+             *
+             * \sa createGraph
              */
-            bool setGraphType( const Types::Class & type );
-            
-            Types::Class graphType() const;
+            void setAdditionalGraphMetadata( const QMultiHash<QUrl, Soprano::Node>& additionalMetadata );
+
+            QMultiHash<QUrl, Soprano::Node> additionalMetadata() const;
 
         protected:
-            
             /**
              * Called when trying to merge a statement which contains a Resource that
              * has not been identified.
-             * 
-             * The default implementation of this creates the resource in the main model.
+             *
+             * The default implementation of this creates the resource in the model.
+             * The resourceUri is generated using createResourceUri.
+             *
+             * If the resolution is supposed to fail, this function returns KUrl().
+             * The reason why resolution failed should also be set with setError()
+             *
+             * \sa createResourceUri
              */
-            virtual Resource resolveUnidentifiedResource( const KUrl & uri );
+            virtual KUrl resolveUnidentifiedResource( const KUrl & uri );
 
             /**
-             * Creates a new graph of type graphType()
+             * Creates a new graph with the additional metadata.
+             * All graphs that are created should be a subtype of nrl:Graph
+             *
+             * \sa additionalMetadata
              */
             virtual KUrl createGraph();
 
             /**
-             * Push the statement into the Nepomuk repository if it doesn't already exist!
+             * Push the statement into the Nepomuk repository.
+             * If a statement with the same subject, predicate and object already
+             * exists in the model, then resolveDuplicate is called.
+             *
+             * \sa resolveDuplicate
+             * \return \c true if pushing the statement was successful
              */
-            void push( const Soprano::Statement & st );
+            bool push( const Soprano::Statement & st );
 
+            /**
+             * If the statement being pushed already exists this method is called.
+             * By default it does nothing which means keeping the old statement
+             *
+             * \return \c true if resolution was successful
+             *         \c false if resolution failed, and merging and should fail
+             */
+            virtual bool resolveDuplicate( const Soprano::Statement & newSt );
+
+            /**
+             * Creates a new resource uri. By default this creates it using the
+             * ResourceManager::instace()->generateUniqueUri("res")
+             */
+            virtual QUrl createResourceUri();
+
+            /**
+             * Creates a new graph uri. By default this creates it using the
+             * ResourceManager::instace()->generateUniqueUri("ctx")
+             */
+            virtual QUrl createGraphUri();
+
+            /**
+             * Returns the graph that is being used to add new statements.
+             * If this graph does not exist it is created using createGraph
+             *
+             * \sa createGraph
+             */
+            KUrl graph();
+
+            /**
+             * Add the statement in the model. By default it just calls
+             * Soprano::Model::addStatement()
+             *
+             * \return \c Soprano::Error::ErrorNone if added to model
+             */
+            virtual Soprano::Error::ErrorCode addStatement( const Soprano::Statement & st );
+            Soprano::Error::ErrorCode addStatement( const Soprano::Node& subject, const Soprano::Node& property,
+                                                    const Soprano::Node& object, const Soprano::Node& graph );
+
+            /**
+             * Resolves the subject and object and gets the object ready for pushing
+             */
+            bool resolveStatement( Soprano::Statement& st );
         private:
             class Private;
             Private * d;
diff --git a/nepomuk/services/backupsync/lib/simpleresource.cpp b/nepomuk/services/backupsync/lib/simpleresource.cpp
deleted file mode 100644
index 17d10c5..0000000
--- a/nepomuk/services/backupsync/lib/simpleresource.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-#include "simpleresource.h"
-
-#include <Soprano/Node>
-#include <Soprano/Graph>
-#include <Soprano/Statement>
-#include <Soprano/StatementIterator>
-
-#include <Nepomuk/Vocabulary/NIE>
-#include <Nepomuk/Vocabulary/NFO>
-#include <Soprano/Vocabulary/RDF>
-
-#include <QtCore/QSharedData>
-
-class Nepomuk::Sync::SimpleResource::Private : public QSharedData {
-public:
-    KUrl uri;
-};
-
-
-Nepomuk::Sync::SimpleResource::SimpleResource()
-    : d( new Nepomuk::Sync::SimpleResource::Private )
-{
-}
-
-Nepomuk::Sync::SimpleResource::SimpleResource(const Nepomuk::Sync::SimpleResource& rhs)
-    : QMultiHash< KUrl, Soprano::Node >(rhs),
-      d( rhs.d )
-{
-}
-
-Nepomuk::Sync::SimpleResource::~SimpleResource()
-{
-}
-
-Nepomuk::Sync::SimpleResource& Nepomuk::Sync::SimpleResource::operator=(const Nepomuk::Sync::SimpleResource& rhs)
-{
-    d = rhs.d;
-    return *this;
-}
-
-bool Nepomuk::Sync::SimpleResource::operator==(const Nepomuk::Sync::SimpleResource& res)
-{
-    return d->uri == res.d->uri &&
-    this->QHash<KUrl, Soprano::Node>::operator==( res );
-}
-
-QList< Soprano::Statement > Nepomuk::Sync::SimpleResource::toStatementList() const
-{
-    QList<Soprano::Statement> list;
-    const QList<KUrl> & keys = uniqueKeys();
-    foreach( const KUrl & key, keys ) {
-        Soprano::Statement st;
-        st.setSubject( Soprano::Node( d->uri ) );
-        st.setPredicate( Soprano::Node( key ) );
-
-        const QList<Soprano::Node>& objects = values( key );
-        foreach( const Soprano::Node & node, objects ) {
-            st.setObject( node );
-            list.append( st );
-        }
-    }
-    return list;
-}
-
-
-bool Nepomuk::Sync::SimpleResource::isFolder() const
-{
-    return values( Soprano::Vocabulary::RDF::type() ).contains( Soprano::Node( Nepomuk::Vocabulary::NFO::Folder() ) );
-}
-
-
-bool Nepomuk::Sync::SimpleResource::isFileDataObject() const
-{
-    return values( Soprano::Vocabulary::RDF::type() ).contains( Soprano::Node( Nepomuk::Vocabulary::NFO::FileDataObject() ) );
-}
-
-
-KUrl Nepomuk::Sync::SimpleResource::nieUrl() const
-{
-    const QHash<KUrl, Soprano::Node>::const_iterator it = constFind( Nepomuk::Vocabulary::NIE::url() );
-    if( it == constEnd() )
-        return KUrl();
-    else
-        return it.value().uri();
-}
-
-
-void Nepomuk::Sync::SimpleResource::setUri(const KUrl& newUri)
-{
-    d->uri = newUri;
-}
-
-KUrl Nepomuk::Sync::SimpleResource::uri() const
-{
-    return d->uri;
-}
-
-QList< Soprano::Node > Nepomuk::Sync::SimpleResource::property(const KUrl& url) const
-{
-    return values(url);
-}
-
-void Nepomuk::Sync::SimpleResource::removeObject(const KUrl& uri)
-{
-    QMutableHashIterator<KUrl, Soprano::Node> iter( *this );
-    while( iter.hasNext() ) {
-        iter.next();
-        
-        if( iter.value().isResource() && iter.value().uri() == uri )
-            iter.remove();
-    }
-}
-
-// static
-Nepomuk::Sync::SimpleResource Nepomuk::Sync::SimpleResource::fromStatementList(const QList< Soprano::Statement >& list)
-{
-    Q_ASSERT( !list.isEmpty() );
-    
-    SimpleResource res;
-    res.setUri( list.first().subject().uri() );
-    
-    foreach( const Soprano::Statement & st, list ) {
-        KUrl pred = st.predicate().uri();
-        Soprano::Node obj = st.object();
-        
-        if( !res.contains( pred, obj ) )
-            res.insert( pred, obj );
-    }
-    
-    return res;
-}
-
-//
-// ResourceHash
-//
-
-// static
-Nepomuk::Sync::ResourceHash Nepomuk::Sync::ResourceHash::fromGraph(const Soprano::Graph& graph)
-{
-    return fromStatementList( graph.listStatements().allStatements() );
-}
-
-// static
-Nepomuk::Sync::ResourceHash Nepomuk::Sync::ResourceHash::fromStatementList(const QList< Soprano::Statement >& allStatements)
-{
-    //
-    // Convert into multi hash for easier look up
-    //
-    QMultiHash<KUrl, Soprano::Statement> stHash;
-    stHash.reserve( allStatements.size() );
-    foreach( const Soprano::Statement & st, allStatements ) {
-        KUrl uri = st.subject().uri();
-        stHash.insert( uri, st );
-    }
-    
-    //
-    // Convert them into a better format --> SimpleResource
-    //
-    const QList<KUrl> & uniqueUris = stHash.uniqueKeys();
-    
-    ResourceHash resources;
-    resources.reserve( uniqueUris.size() );
-    
-    foreach( const KUrl & resUri, uniqueUris ) {
-        SimpleResource res = SimpleResource::fromStatementList( stHash.values( resUri ) );
-        resources.insert( res.uri(), res );
-    }
-    
-    return resources;
-}
-
-
-QList< Soprano::Statement > Nepomuk::Sync::ResourceHash::toStatementList() const
-{
-    QList<Soprano::Statement> stList;
-    Q_FOREACH( const KUrl& uri, uniqueKeys() ) {
-        const SimpleResource & res = value( uri );
-        stList += res.toStatementList();
-    }
-
-    return stList;
-}
diff --git a/nepomuk/services/backupsync/lib/simpleresource.h b/nepomuk/services/backupsync/lib/simpleresource.h
deleted file mode 100644
index f95b0e1..0000000
--- a/nepomuk/services/backupsync/lib/simpleresource.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-#ifndef NEPOMUK_SIMPLERESOURCEH_H
-#define NEPOMUK_SIMPLERESOURCEH_H
-
-#include <KUrl>
-
-#include <QtCore/QList>
-#include <QtCore/QHash>
-#include <QtCore/QSharedDataPointer>
-
-#include "nepomuksync_export.h"
-
-namespace Soprano {
-    class Node;
-    class Statement;
-    class Graph;
-}
-
-namespace Nepomuk {
-    namespace Sync {
-
-        /**
-         * \class SimpleResource simpleresource.h
-         *
-         * A SimpleResource is a convenient way of storing a set of properties and objects for
-         * a common subject. This class does not represent an actual resource present in the
-         * repository. It's just a collection of in-memory statements.
-         *
-         * Interally it uses a multi-hash to store the properties and objects.
-         * 
-         * \author Vishesh Handa <handa.vish@gmail.com>
-         */
-        class NEPOMUKSYNC_EXPORT SimpleResource : public QMultiHash<KUrl, Soprano::Node>
-        {
-        public :
-            SimpleResource();
-            SimpleResource( const SimpleResource & rhs );
-            virtual ~SimpleResource();
-
-            /**
-             * It uses the the first element's subject as the uri and ignores all further subjects.
-             * Please make sure all the subjects are the same cause no kind of checks are made.
-             */
-            static SimpleResource fromStatementList(const QList<Soprano::Statement> & list);
-            
-            QList<Soprano::Statement> toStatementList() const;
-
-            bool isFileDataObject() const;
-            bool isFolder() const;
-            KUrl nieUrl() const;
-
-            KUrl uri() const;
-            void setUri( const KUrl & newUri );
-
-            
-            QList<Soprano::Node> property( const KUrl & url ) const;
-
-            /**
-             * Removes all the statements whose object is \p uri
-             */
-            void removeObject( const KUrl & uri );
-
-            SimpleResource& operator=( const SimpleResource & rhs );
-            bool operator==( const SimpleResource & res );
-        private:
-            class Private;
-            QSharedDataPointer<Private> d;
-        };
-
-        /**
-         * \class ResourceHash simpleresource.h
-         *
-         * A SimpleResource is a convenient way of representing a list of Soprano::Statements
-         * or a Soprano::Graph.
-         * It provides easy lookup of resources.
-         *
-         * \author Vishesh Handa <handa.vish@gmail.com>
-         */
-        class NEPOMUKSYNC_EXPORT ResourceHash : public QHash<KUrl, SimpleResource> {
-        public :
-            static ResourceHash fromStatementList( const QList<Soprano::Statement> & list );
-            static ResourceHash fromGraph( const Soprano::Graph & graph );
-
-            QList<Soprano::Statement> toStatementList() const;
-        };
-        
-    }
-}
-#endif // NEPOMUK_SIMPLERESOURCEH_H
diff --git a/nepomuk/services/backupsync/lib/syncresource.cpp b/nepomuk/services/backupsync/lib/syncresource.cpp
new file mode 100644
index 0000000..72f4701
--- /dev/null
+++ b/nepomuk/services/backupsync/lib/syncresource.cpp
@@ -0,0 +1,236 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "syncresource.h"
+
+#include <Soprano/Node>
+#include <Soprano/Graph>
+#include <Soprano/Statement>
+#include <Soprano/StatementIterator>
+
+#include <Nepomuk/Vocabulary/NIE>
+#include <Nepomuk/Vocabulary/NFO>
+#include <Soprano/Vocabulary/RDF>
+
+#include <QtCore/QSharedData>
+
+class Nepomuk::Sync::SyncResource::Private : public QSharedData {
+public:
+    KUrl uri;
+};
+
+
+Nepomuk::Sync::SyncResource::SyncResource()
+    : d( new Nepomuk::Sync::SyncResource::Private )
+{
+}
+
+Nepomuk::Sync::SyncResource::SyncResource(const KUrl& uri)
+    : d( new Nepomuk::Sync::SyncResource::Private )
+{
+    setUri( uri );
+}
+
+Nepomuk::Sync::SyncResource::SyncResource(const Nepomuk::Sync::SyncResource& rhs)
+    : QMultiHash< KUrl, Soprano::Node >(rhs),
+      d( rhs.d )
+{
+}
+
+Nepomuk::Sync::SyncResource::~SyncResource()
+{
+}
+
+Nepomuk::Sync::SyncResource& Nepomuk::Sync::SyncResource::operator=(const Nepomuk::Sync::SyncResource& rhs)
+{
+    d = rhs.d;
+    return *this;
+}
+
+bool Nepomuk::Sync::SyncResource::operator==(const Nepomuk::Sync::SyncResource& res) const
+{
+    return d->uri == res.d->uri &&
+    this->QHash<KUrl, Soprano::Node>::operator==( res );
+}
+
+QList< Soprano::Statement > Nepomuk::Sync::SyncResource::toStatementList() const
+{
+    QList<Soprano::Statement> list;
+    const QList<KUrl> & keys = uniqueKeys();
+    foreach( const KUrl & key, keys ) {
+        Soprano::Statement st;
+        Soprano::Node sub = d->uri.url().startsWith("_:") ? Soprano::Node(d->uri.url().mid(2)) : d->uri;
+        st.setSubject( sub );
+        st.setPredicate( Soprano::Node( key ) );
+
+        const QList<Soprano::Node>& objects = values( key );
+        foreach( const Soprano::Node & node, objects ) {
+            st.setObject( node );
+            list.append( st );
+        }
+    }
+    return list;
+}
+
+
+bool Nepomuk::Sync::SyncResource::isFolder() const
+{
+    return values( Soprano::Vocabulary::RDF::type() ).contains( Soprano::Node( Nepomuk::Vocabulary::NFO::Folder() ) );
+}
+
+
+bool Nepomuk::Sync::SyncResource::isFileDataObject() const
+{
+    return values( Soprano::Vocabulary::RDF::type() ).contains( Soprano::Node( Nepomuk::Vocabulary::NFO::FileDataObject() ) );
+}
+
+
+KUrl Nepomuk::Sync::SyncResource::nieUrl() const
+{
+    const QHash<KUrl, Soprano::Node>::const_iterator it = constFind( Nepomuk::Vocabulary::NIE::url() );
+    if( it == constEnd() )
+        return KUrl();
+    else
+        return it.value().uri();
+}
+
+
+void Nepomuk::Sync::SyncResource::setUri(const Soprano::Node& node)
+{
+    if( node.isResource() ) {
+        d->uri = node.uri();
+    }
+    else if( node.isBlank() ) {
+        d->uri = KUrl( node.toN3() );
+    }
+}
+
+KUrl Nepomuk::Sync::SyncResource::uri() const
+{
+    return d->uri;
+}
+
+QList< Soprano::Node > Nepomuk::Sync::SyncResource::property(const KUrl& url) const
+{
+    return values(url);
+}
+
+void Nepomuk::Sync::SyncResource::removeObject(const KUrl& uri)
+{
+    QMutableHashIterator<KUrl, Soprano::Node> iter( *this );
+    while( iter.hasNext() ) {
+        iter.next();
+
+        if( iter.value().isResource() && iter.value().uri() == uri )
+            iter.remove();
+    }
+}
+
+namespace {
+    // Blank nodes are stored as "_:identifier" in urls
+    QUrl getUri( const Soprano::Node & n ) {
+        if( n.isBlank() )
+            return QUrl( n.toN3() );
+        else
+            return n.uri();
+    }
+}
+// static
+Nepomuk::Sync::SyncResource Nepomuk::Sync::SyncResource::fromStatementList(const QList< Soprano::Statement >& list)
+{
+    if( list.isEmpty() )
+        return SyncResource();
+
+    SyncResource res;
+    Soprano::Node subject = list.first().subject();
+    res.setUri( getUri(subject) );
+
+    foreach( const Soprano::Statement & st, list ) {
+        if( st.subject() != subject )
+            continue;
+
+        KUrl pred = st.predicate().uri();
+        Soprano::Node obj = st.object();
+
+        if( !res.contains( pred, obj ) )
+            res.insert( pred, obj );
+    }
+
+    return res;
+}
+
+//
+// ResourceHash
+//
+
+// static
+Nepomuk::Sync::ResourceHash Nepomuk::Sync::ResourceHash::fromGraph(const Soprano::Graph& graph)
+{
+    return fromStatementList( graph.listStatements().allStatements() );
+}
+
+// static
+Nepomuk::Sync::ResourceHash Nepomuk::Sync::ResourceHash::fromStatementList(const QList< Soprano::Statement >& allStatements)
+{
+    //
+    // Convert into multi hash for easier look up
+    //
+    QMultiHash<KUrl, Soprano::Statement> stHash;
+    stHash.reserve( allStatements.size() );
+    foreach( const Soprano::Statement & st, allStatements ) {
+        KUrl uri = getUri( st.subject() );
+        stHash.insert( uri, st );
+    }
+
+    //
+    // Convert them into a better format --> SyncResource
+    //
+    const QList<KUrl> & uniqueUris = stHash.uniqueKeys();
+
+    ResourceHash resources;
+    resources.reserve( uniqueUris.size() );
+
+    foreach( const KUrl & resUri, uniqueUris ) {
+        SyncResource res = SyncResource::fromStatementList( stHash.values( resUri ) );
+        resources.insert( res.uri(), res );
+    }
+
+    return resources;
+}
+
+
+QList< Soprano::Statement > Nepomuk::Sync::ResourceHash::toStatementList() const
+{
+    QList<Soprano::Statement> stList;
+    Q_FOREACH( const KUrl& uri, uniqueKeys() ) {
+        const SyncResource & res = value( uri );
+        stList += res.toStatementList();
+    }
+
+    return stList;
+}
+
+
+bool Nepomuk::Sync::SyncResource::isValid() const
+{
+    return !d->uri.isEmpty() && !isEmpty();
+}
diff --git a/nepomuk/services/backupsync/lib/syncresource.h b/nepomuk/services/backupsync/lib/syncresource.h
new file mode 100644
index 0000000..77f3b90
--- /dev/null
+++ b/nepomuk/services/backupsync/lib/syncresource.h
@@ -0,0 +1,118 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef NEPOMUK_SIMPLERESOURCEH_H
+#define NEPOMUK_SIMPLERESOURCEH_H
+
+#include <KUrl>
+
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtCore/QSharedDataPointer>
+
+#include "nepomuksync_export.h"
+
+namespace Soprano {
+    class Node;
+    class Statement;
+    class Graph;
+}
+
+namespace Nepomuk {
+    namespace Sync {
+
+        /**
+         * \class SyncResource syncresource.h
+         *
+         * A SyncResource is a convenient way of storing a set of properties and objects for
+         * a common subject. This class does not represent an actual resource present in the
+         * repository. It's just a collection of in-memory statements.
+         *
+         * Interally it uses a multi-hash to store the properties and objects.
+         * 
+         * \author Vishesh Handa <handa.vish@gmail.com>
+         */
+        class NEPOMUKSYNC_EXPORT SyncResource : public QMultiHash<KUrl, Soprano::Node>
+        {
+        public :
+            SyncResource();
+            SyncResource( const KUrl & uri );
+            SyncResource( const SyncResource & rhs );
+            virtual ~SyncResource();
+
+            /**
+             * It uses the the first element's subject as the uri and ignores all further subjects.
+             * Please make sure all the subjects are the same cause no kind of checks are made.
+             */
+            static SyncResource fromStatementList(const QList<Soprano::Statement> & list);
+            
+            QList<Soprano::Statement> toStatementList() const;
+
+            bool isFileDataObject() const;
+            bool isFolder() const;
+            KUrl nieUrl() const;
+
+            KUrl uri() const;
+
+            /**
+             * If \p node is resource node the uri is set to the node's uri
+             * Otherwise if \p node is a blank node then the uri
+             * is set to its identifier
+             */
+            void setUri( const Soprano::Node & node );
+            
+            QList<Soprano::Node> property( const KUrl & url ) const;
+
+            /**
+             * Removes all the statements whose object is \p uri
+             */
+            void removeObject( const KUrl & uri );
+
+            SyncResource& operator=( const SyncResource & rhs );
+            bool operator==( const SyncResource & res ) const;
+
+            bool isValid() const;
+        private:
+            class Private;
+            QSharedDataPointer<Private> d;
+        };
+
+        /**
+         * \class ResourceHash syncresource.h
+         *
+         * A SyncResource is a convenient way of representing a list of Soprano::Statements
+         * or a Soprano::Graph.
+         * It provides easy lookup of resources.
+         *
+         * \author Vishesh Handa <handa.vish@gmail.com>
+         */
+        class NEPOMUKSYNC_EXPORT ResourceHash : public QHash<KUrl, SyncResource> {
+        public :
+            static ResourceHash fromStatementList( const QList<Soprano::Statement> & list );
+            static ResourceHash fromGraph( const Soprano::Graph & graph );
+
+            QList<Soprano::Statement> toStatementList() const;
+        };
+        
+    }
+}
+#endif // NEPOMUK_SIMPLERESOURCEH_H
diff --git a/nepomuk/services/backupsync/service/CMakeLists.txt b/nepomuk/services/backupsync/service/CMakeLists.txt
index 252c850..41ef5df 100644
--- a/nepomuk/services/backupsync/service/CMakeLists.txt
+++ b/nepomuk/services/backupsync/service/CMakeLists.txt
@@ -7,7 +7,7 @@ find_package(Nepomuk REQUIRED)
 include(SopranoAddOntology)
 
 add_definitions(-DDISABLE_NEPOMUK_LEGACY=1)
-add_definitions(-DKDE_DEFAULT_DEBUG_AREA=300106)
+#add_definitions(-DKDE_DEFAULT_DEBUG_AREA=300106)
 
 include (KDE4Defaults)
 
@@ -22,32 +22,28 @@ include_directories(
   )
 
 set( BackupSyncService_SRCS
-  identifier.cpp
-  merger.cpp
   logstorage.cpp
   backupsyncservice.cpp
   diffgenerator.cpp
   syncmanager.cpp
   backupmanager.cpp
   dbusoperators.cpp
-  changelogmerger.cpp
-  syncfileidentifier.cpp
   tools.cpp
-  resourcelog.cpp
   changelog.cpp
   changelogrecord.cpp
   identificationset.cpp
   syncfile.cpp
+  backupgenerationjob.cpp
 )
 
 #--------- Adaptors ---------#
 
-qt4_add_dbus_adaptor( BackupSyncService_SRCS
-                      ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.identifier.xml
-                      identifier.h Nepomuk::Identifier )
-qt4_add_dbus_adaptor( BackupSyncService_SRCS
-                      ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.merger.xml
-                      merger.h Nepomuk::Merger )
+# qt4_add_dbus_adaptor( BackupSyncService_SRCS
+#                       ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.identifier.xml
+#                       identifier.h Nepomuk::Identifier )
+# qt4_add_dbus_adaptor( BackupSyncService_SRCS
+#                       ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.merger.xml
+#                       merger.h Nepomuk::Merger )
 qt4_add_dbus_adaptor( BackupSyncService_SRCS
                       ../../../interfaces/org.kde.nepomuk.services.nepomukbackupsync.backupmanager.xml
                       backupmanager.h Nepomuk::BackupManager )
@@ -58,7 +54,7 @@ qt4_add_dbus_adaptor( BackupSyncService_SRCS
 #--------- Ontologies -------#
 soprano_add_ontology(BackupSyncService_SRCS
    ${CMAKE_CURRENT_SOURCE_DIR}/../../../ontologies/nrio.trig
-  "backupsync"
+  "NRIO"
   "Nepomuk::Vocabulary"
   "trig")
 
diff --git a/nepomuk/services/backupsync/service/backupgenerationjob.cpp b/nepomuk/services/backupsync/service/backupgenerationjob.cpp
new file mode 100644
index 0000000..94514a3
--- /dev/null
+++ b/nepomuk/services/backupsync/service/backupgenerationjob.cpp
@@ -0,0 +1,45 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "backupgenerationjob.h"
+#include "tools.h"
+
+#include <QtCore/QTimer>
+
+Nepomuk::BackupGenerationJob::BackupGenerationJob(const QUrl& url, QObject* parent)
+    : KJob(parent),
+      m_url( url )
+{
+}
+
+void Nepomuk::BackupGenerationJob::start()
+{
+    QTimer::singleShot( 0, this, SLOT(doWork()) );
+}
+
+void Nepomuk::BackupGenerationJob::doWork()
+{
+    Nepomuk::saveBackupSyncFile( m_url );
+    emitResult();
+}
+
+
+
diff --git a/nepomuk/services/backupsync/service/backupgenerationjob.h b/nepomuk/services/backupsync/service/backupgenerationjob.h
new file mode 100644
index 0000000..63837c0
--- /dev/null
+++ b/nepomuk/services/backupsync/service/backupgenerationjob.h
@@ -0,0 +1,44 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011  Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef BACKUPGENERATOR_H
+#define BACKUPGENERATOR_H
+
+#include <KJob>
+#include <QtCore/QUrl>
+
+namespace Nepomuk {
+
+    class BackupGenerationJob : public KJob
+    {
+        Q_OBJECT
+    public:
+        BackupGenerationJob(const QUrl& url, QObject* parent = 0);
+        virtual void start();
+
+    private slots:
+        void doWork();
+
+    private:
+        QUrl m_url;
+    };
+}
+
+#endif // BACKUPGENERATOR_H
diff --git a/nepomuk/services/backupsync/service/backupmanager.cpp b/nepomuk/services/backupsync/service/backupmanager.cpp
index 3b39c5b..8cbc3b2 100644
--- a/nepomuk/services/backupsync/service/backupmanager.cpp
+++ b/nepomuk/services/backupsync/service/backupmanager.cpp
@@ -23,12 +23,12 @@
 #include "backupmanager.h"
 #include "backupmanageradaptor.h"
 #include "logstorage.h"
-#include "identifier.h"
 #include "tools.h"
 
 #include "changelog.h"
 #include "syncfile.h"
 #include "identificationset.h"
+#include "backupgenerationjob.h"
 
 #include <QtDBus/QDBusConnection>
 #include <QtCore/QListIterator>
@@ -45,9 +45,8 @@
 #include <KCalendarSystem>
 
 
-Nepomuk::BackupManager::BackupManager(Nepomuk::Identifier* ident, QObject* parent)
+Nepomuk::BackupManager::BackupManager(QObject* parent)
     : QObject( parent ),
-      m_identifier( ident ),
       m_config( "nepomukbackuprc" )
 {
     new BackupManagerAdaptor( this );
@@ -86,19 +85,10 @@ void Nepomuk::BackupManager::backup(const QString& oldUrl)
 
     QFile::remove( url );
 
-    saveBackupSyncFile( url );
-    emit backupDone();
-}
-
+    KJob * job = new BackupGenerationJob( url, this );
 
-int Nepomuk::BackupManager::restore(const QString& oldUrl)
-{
-    //TODO: Some kind of error checking!
-    QString url = oldUrl;
-    if( url.isEmpty() )
-        url = KStandardDirs::locateLocal( "data", "nepomuk/backupsync/backup" );
-    
-    return m_identifier->process( SyncFile(url) );
+    connect( job, SIGNAL(finished(KJob*)), this, SLOT(slotBackupDone(KJob*)) );
+    job->start();
 }
 
 void Nepomuk::BackupManager::automatedBackup()
@@ -114,10 +104,10 @@ void Nepomuk::BackupManager::slotConfigDirty()
 {
     kDebug();
     m_config.reparseConfiguration();
-    
+
     QString freq = m_config.group("Backup").readEntry( "backup frequency", QString("disabled") );
     kDebug() << "Frequency : " << freq;
-    
+
     if( freq == QLatin1String("disabled") ) {
         kDebug() << "Auto Backups Disabled";
         m_timer.stop();
@@ -128,7 +118,7 @@ void Nepomuk::BackupManager::slotConfigDirty()
     m_backupTime = QTime::fromString( timeString, Qt::ISODate );
 
     if( freq == QLatin1String("daily") ) {
-        m_daysBetweenBackups = 0;    
+        m_daysBetweenBackups = 0;
     }
 
     else if( freq == QLatin1String("weekly") ) {
@@ -159,7 +149,7 @@ void Nepomuk::BackupManager::slotConfigDirty()
     else if( freq == QLatin1String("monthly") ) {
         //TODO: Implement me!
     }
-    
+
     m_maxBackups = m_config.group("Backup").readEntry<int>("max backups", 1);
 
     // Remove old timers and start new
@@ -181,7 +171,7 @@ void Nepomuk::BackupManager::resetTimer()
     if( dateTime < current ) {
         dateTime = dateTime.addDays( 1 );
     }
-    
+
     int msecs = current.msecsTo( dateTime );
 
     m_timer.stop();
@@ -202,6 +192,13 @@ void Nepomuk::BackupManager::removeOldBackups()
     }
 }
 
+void Nepomuk::BackupManager::slotBackupDone(KJob* job)
+{
+    if( !job->error() ) {
+        emit backupDone();
+    }
+}
+
 
 
 #include "backupmanager.moc"
diff --git a/nepomuk/services/backupsync/service/backupmanager.h b/nepomuk/services/backupsync/service/backupmanager.h
index bed862b..911990c 100644
--- a/nepomuk/services/backupsync/service/backupmanager.h
+++ b/nepomuk/services/backupsync/service/backupmanager.h
@@ -28,30 +28,26 @@
 #include <QtCore/QTimer>
 
 #include <KConfig>
+#include <KJob>
 
 namespace Nepomuk {
 
-    class Identifier;
-    class Merger;
-
     class BackupManager : public QObject
     {
         Q_OBJECT
         Q_CLASSINFO("D-Bus Interface", "org.kde.nepomuk.services.nepomukbackupsync.BackupManager")
 
     public:
-        BackupManager(Nepomuk::Identifier* ident, QObject* parent = 0);
+        BackupManager(QObject* parent = 0);
         virtual ~BackupManager();
 
     public slots:
         void backup( const QString & url = QString() );
-        int restore( const QString & url = QString() );
 
     signals:
         void backupDone();
-        
+
     private:
-        Identifier * m_identifier;
         QString m_backupLocation;
 
         QTime m_backupTime;
@@ -63,10 +59,11 @@ namespace Nepomuk {
         QTimer m_timer;
         void resetTimer();
         void removeOldBackups();
-        
+
     private slots:
         void slotConfigDirty();
         void automatedBackup();
+        void slotBackupDone(KJob * job);
     };
 
 }
diff --git a/nepomuk/services/backupsync/service/backupsyncservice.cpp b/nepomuk/services/backupsync/service/backupsyncservice.cpp
index 7f5df1b..8dc2ae5 100644
--- a/nepomuk/services/backupsync/service/backupsyncservice.cpp
+++ b/nepomuk/services/backupsync/service/backupsyncservice.cpp
@@ -22,8 +22,6 @@
 
 #include "backupsyncservice.h"
 #include "diffgenerator.h"
-#include "identifier.h"
-#include "merger.h"
 #include "syncmanager.h"
 #include "backupmanager.h"
 #include "dbusoperators.h"
@@ -44,19 +42,9 @@ Nepomuk::BackupSyncService::BackupSyncService( QObject* parent, const QList< QVa
     kDebug();
 
     m_diffGenerator = new DiffGenerator( this );
-    m_identifier = new Identifier( this );
-    m_merger = new Merger( this );
 
-    m_syncManager = new SyncManager( m_identifier, this );
-    m_backupManager = new BackupManager( m_identifier, this );
-
-    // IMPORTANT : We've used "Nepomuk::ChangeLog" in the string cause in the slots, signals, and
-    // connect statement we're using Nepomuk::ChangeLog, NOT ChangeLog
-    qRegisterMetaType<Nepomuk::ChangeLog>("Nepomuk::ChangeLog");
-
-    registerMetaTypes();
-    connect( m_identifier, SIGNAL( processed( Nepomuk::ChangeLog ) ),
-             m_merger, SLOT( process( Nepomuk::ChangeLog ) ) );
+    m_syncManager = new SyncManager( this );
+    m_backupManager = new BackupManager( this );
 }
 
 Nepomuk::BackupSyncService::~BackupSyncService()
@@ -67,7 +55,6 @@ void Nepomuk::BackupSyncService::test()
 {
     //QUrl url("/home/vishesh/syncnew");
 
-    m_identifier->test();
     /*
     LogFile lf;
     lf.load(QUrl("file:///home/vishesh/ident.trig"));
diff --git a/nepomuk/services/backupsync/service/backupsyncservice.h b/nepomuk/services/backupsync/service/backupsyncservice.h
index fc38521..aa8face 100644
--- a/nepomuk/services/backupsync/service/backupsyncservice.h
+++ b/nepomuk/services/backupsync/service/backupsyncservice.h
@@ -28,8 +28,6 @@
 namespace Nepomuk {
 
     class DiffGenerator;
-    class Identifier;
-    class Merger;
 
     class SyncManager;
     class BackupManager;
@@ -47,8 +45,6 @@ namespace Nepomuk {
 
     private:
         DiffGenerator * m_diffGenerator;
-        Identifier * m_identifier;
-        Merger * m_merger;
 
         SyncManager * m_syncManager;
         BackupManager * m_backupManager;
diff --git a/nepomuk/services/backupsync/service/changelogmerger.cpp b/nepomuk/services/backupsync/service/changelogmerger.cpp
deleted file mode 100644
index 2c259a4..0000000
--- a/nepomuk/services/backupsync/service/changelogmerger.cpp
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-
-#include "changelogmerger.h"
-#include "backupsync.h"
-#include "logstorage.h"
-
-#include <algorithm>
-
-#include <QtCore/QMultiHash>
-#include <QtCore/QHashIterator>
-#include <QtCore/QThread>
-
-#include <Soprano/Vocabulary/NAO>
-#include <Soprano/Vocabulary/NRL>
-#include <Soprano/Vocabulary/RDF>
-#include <Nepomuk/Vocabulary/NIE>
-#include <Nepomuk/Vocabulary/NFO>
-
-#include <Soprano/Node>
-#include <Soprano/Statement>
-#include <Soprano/Model>
-#include <Soprano/QueryResultIterator>
-#include <Soprano/StatementIterator>
-#include <Soprano/NodeIterator>
-
-#include <Nepomuk/Resource>
-#include <Nepomuk/Variant>
-#include <Nepomuk/Types/Property>
-#include <Nepomuk/ResourceManager>
-
-#include <KDebug>
-
-int Nepomuk::ChangeLogMerger::NextId = 0;
-
-Nepomuk::ChangeLogMerger::ChangeLogMerger(Nepomuk::ChangeLog log)
-    : ResourceMerger(),
-      m_logFile( log )
-{
-    m_id = NextId++;
-}
-
-int Nepomuk::ChangeLogMerger::id()
-{
-    return m_id;
-}
-
-void Nepomuk::ChangeLogMerger::load()
-{
-    kDebug() << "Loading the ChangeLog..." << m_logFile.size();
-    m_hash = ResourceLogMap::fromChangeLog( m_logFile );
-    
-    // The records are stored according to dateTime
-    Q_ASSERT( m_logFile.toList().isEmpty() == false );
-    m_minDateTime = m_logFile.toList().first().dateTime();
-}
-
-namespace {
-
-    //
-    // Cache the results. This could have very bad consequences if someone updates the ontology
-    // when the service is running
-    //
-    static QSet<KUrl> nonMergeable;
-    bool isMergeable( const KUrl & prop, Soprano::Model * model ) {
-
-        if( nonMergeable.contains( prop ) )
-            return false;
-
-        QString query = QString::fromLatin1( "ask { %1 %2 \"false\"^^xsd:boolean . }" )
-                        .arg( Soprano::Node::resourceToN3( prop ) )
-                        .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::backupsync::mergeable() ) );
-
-        bool isMergeable = !model->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue();
-
-        if( !isMergeable ) {
-            nonMergeable.insert( prop );
-            return false;
-        }
-        return true;
-    }
-
-    QList<Nepomuk::ChangeLogRecord> getRecords( const Nepomuk::ResourceLogMap & hash, const KUrl resUri, const KUrl & propUri ) {
-
-        Nepomuk::ResourceLogMap::const_iterator it = hash.constFind( resUri );
-        if( it == hash.constEnd() ) {
-            return QList<Nepomuk::ChangeLogRecord>();
-        }
-
-        return it->prop.values( propUri );
-    }
-}
-
-//TODO: Add completed signal
-void Nepomuk::ChangeLogMerger::mergeChangeLog()
-{
-    m_theGraph = createGraph();
-    
-    kDebug();
-    const Types::Property mergeableProperty( Nepomuk::Vocabulary::backupsync::mergeable() );
-
-    //
-    // Get own changeLog
-    //
-    kDebug() << "minDateTime : " << m_minDateTime;
-    ChangeLog ownLog = LogStorage::instance()->getChangeLog( m_minDateTime );
-    kDebug() << "own Log : " << ownLog.size();
-
-    // Get our and their hash
-    // ownHash = current local hash from system's ChangeLog
-    // theirHash = derived from external ChangeLog
-    ResourceLogMap ownHash = ResourceLogMap::fromChangeLog( ownLog );
-    ResourceLogMap & theirHash = m_hash;
-    
-    kDebug() << "own Hash : " << ownHash.size();
-    kDebug() << "their hash : " << theirHash.size();
-
-    QHashIterator<KUrl, ResourceLog> it( theirHash );
-    while( it.hasNext() ) {
-        it.next();
-
-        // Check for resource deletions
-        if( handleResourceDeletion( it.key() ) )
-            continue;
-
-        const KUrl & resUri = it.key();
-        const ResourceLog & resLog = it.value();
-
-        kDebug() << "Resolving " << resUri;
-
-        const QList<KUrl> & properties = resLog.prop.uniqueKeys();
-        foreach( const KUrl & propUri, properties ) {
-            kDebug() << propUri;
-
-            if( !isMergeable( propUri, model() ) ) {
-                kDebug() << propUri << " is non Mergeable - IGNORING";
-                continue;
-            }
-
-            Nepomuk::Types::Property prop( propUri );
-            int cardinality = prop.maxCardinality();
-
-            QList<ChangeLogRecord> theirRecords = resLog.prop.values( propUri );
-            QList<ChangeLogRecord> ownRecords = getRecords( ownHash, resUri, propUri );
-            //kDebug() << "own Records : " << ownRecords.size();
-
-            // This case shouldn't ever happen, but just to be sure
-            if( theirRecords.empty() )
-                continue;
-
-            if( cardinality == 1 ) {
-                resolveSingleCardinality( theirRecords, ownRecords );
-            }
-            else {
-                resolveMultipleCardinality( theirRecords, ownRecords );
-            }
-        }
-
-        //if( !rs.propHash.isEmpty() )
-        //    m_jobs.append( rs );
-    }
-    //theirHash.clear();
-    //kDebug() << "Done with merge resolution : " << m_jobs.size();
-
-    //processJobs();
-}
-
-
-namespace {
-
-    Nepomuk::ChangeLogRecord maxRecord( const QList<Nepomuk::ChangeLogRecord> & records ) {
-        QList<Nepomuk::ChangeLogRecord>::const_iterator it = std::max_element( records.begin(), records.end() );
-        if( it != records.constEnd() )
-            return *it;
-        return Nepomuk::ChangeLogRecord();
-    }
-}
-
-
-void Nepomuk::ChangeLogMerger::resolveSingleCardinality(const QList< Nepomuk::ChangeLogRecord >& theirRecords, const QList< Nepomuk::ChangeLogRecord >& ownRecords)
-{
-    kDebug() << "O: " << ownRecords.size() << " " << "T:" << theirRecords.size();
-
-    //Find max on the basis of time stamp
-    ChangeLogRecord theirMax = maxRecord( theirRecords );
-    ChangeLogRecord ownMax = maxRecord( ownRecords );
-    kDebug() << "TheirMax : "<< theirMax.toString();
-    kDebug() << "OwnMax " << ownMax.toString();
-
-    if( theirMax > ownMax ) {
-        Soprano::Statement statement( theirMax.st().subject(), theirMax.st().predicate(),
-                                      Soprano::Node(), Soprano::Node() );
-
-        if( theirMax.added() ) {
-            Soprano::Node object = theirMax.st().object();
-            kDebug() << "Resolved - Adding " << object;
-
-            if( !model()->containsAnyStatement( statement ) ) {
-                statement.setObject( object );
-                statement.setContext( m_theGraph );
-                model()->addStatement( statement );
-            }
-        }
-        else {
-            kDebug() << "Resolved - Removing";
-            model()->removeAllStatements( statement );
-        }
-    }
-}
-
-namespace {
-
-    struct MergeData {
-        bool added;
-        QDateTime dateTime;
-
-        MergeData( bool add, const QDateTime & dt )
-            : added( add ),
-              dateTime( dt )
-        {}
-    };
-
-
-}
-
-void Nepomuk::ChangeLogMerger::resolveMultipleCardinality( const QList<Nepomuk::ChangeLogRecord>& theirRecords, const QList<Nepomuk::ChangeLogRecord>& ownRecords)
-{
-    kDebug() << "MULTIPLE";
-    kDebug() << "O: " << ownRecords.size() << " " << "T:" << theirRecords.size();
-
-    const Soprano::Statement& reference = theirRecords.first().st();
-    Soprano::Statement baseStatement( reference.subject(), reference.predicate(), Soprano::Node(), Soprano::Node() );
-
-    //
-    // Merge both record lists
-    //
-    //TODO: Optimize merging - use merge sort or something equivilant
-    QList<ChangeLogRecord> records = ownRecords;
-    records << theirRecords;
-    qSort( records );
-
-    QHash<Soprano::Node, MergeData> hash;
-    foreach( const ChangeLogRecord rec, records ) {
-        Soprano::Node object = rec.st().object();
-        QHash<Soprano::Node, MergeData>::const_iterator it = hash.constFind( object );
-        if( it == hash.constEnd() ) {
-            hash.insert( object, MergeData( rec.added(), rec.dateTime() ) );
-        }
-        else {
-            // +ve after -ve
-            if( rec.added() == true && it.value().added == false ) {
-                hash.remove( object );
-                hash.insert( object, MergeData( rec.added(), rec.dateTime() ) );
-            }
-            // -ve after +ve
-            else if( rec.added() == false && it.value().added == true ) {
-                hash.remove( object );
-            }
-            // +ve after +ve
-            // -ve after -ve
-            //    Do nothing
-        }
-    }
-
-    //
-    // Do the actual merging
-    //
-    QHashIterator<Soprano::Node, MergeData> it( hash );
-    while( it.hasNext() ) {
-        it.next();
-
-        Soprano::Statement st( baseStatement );
-        st.setObject( it.key() );
-
-        MergeData data = it.value();
-        if( data.added == true ) {
-            if( !model()->containsAnyStatement( st ) ) {
-                st.setContext( m_theGraph );
-                model()->addStatement( st );
-                kDebug() << "adding - " << st;
-            }
-        }
-        else {
-            kDebug() << "removing " << st;
-            model()->removeAllStatements( st );
-        }
-    }
-
-    m_multipleMergers.append( Soprano::Statement( baseStatement.subject(),
-                                                  baseStatement.predicate(),
-                                                  Soprano::Node() ) );
-}
-
-QList< Soprano::Statement > Nepomuk::ChangeLogMerger::multipleMergers() const
-{
-    return m_multipleMergers;
-}
-
-bool Nepomuk::ChangeLogMerger::handleResourceDeletion(const KUrl& resUri)
-{
-    ResourceLog & log = m_hash[ resUri ];
-    const KUrl& rdfTypeProp = Soprano::Vocabulary::RDF::type();
-
-    QList<ChangeLogRecord> records = log.prop.values( rdfTypeProp );
-    if( records.empty() )
-        return false;
-
-    //
-    // Check if rdf:type is being removed
-    //
-    bool removed = false;
-    foreach( const ChangeLogRecord & r, records ) {
-        if( !r.added() ) {
-            removed = true;
-            break;
-        }
-    }
-    if( !removed )
-        return false;
-
-    // If removed, remove all records and delete the resource
-    m_hash.remove( resUri );
-    Resource res( resUri );
-    res.remove();
-    return true;
-}
\ No newline at end of file
diff --git a/nepomuk/services/backupsync/service/changelogmerger.h b/nepomuk/services/backupsync/service/changelogmerger.h
deleted file mode 100644
index b968342..0000000
--- a/nepomuk/services/backupsync/service/changelogmerger.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-
-#ifndef CHANGELOGMERGER_H
-#define CHANGELOGMERGER_H
-
-#include "resourcemerger.h"
-#include "simpleresource.h"
-#include "changelog.h"
-#include "resourcelog.h"
-
-#include <QtCore/QDateTime>
-
-namespace Nepomuk {
-    class ChangeLogMerger : public Sync::ResourceMerger
-    {
-    public:
-        ChangeLogMerger( ChangeLog log );
-
-        int id();
-        void mergeChangeLog();
-
-        /**
-         * Converts the contents of the LogFile into the MergeHash
-         * The LogFile can get rather large, so this could be a time consuming
-         * process.
-         */
-        void load();
-
-        QList<Soprano::Statement> multipleMergers() const;
-        
-    private:
-        ChangeLog m_logFile;
-        QDateTime m_minDateTime;
-        
-        QList<Sync::SimpleResource> m_jobs;
-        QList<Soprano::Statement> m_multipleMergers;
-        
-        /// Contains all the records from the LogFile
-        ResourceLogMap m_hash;
-        
-        static int NextId;
-        int m_id;
-
-        KUrl m_theGraph;
-
-        /**
-         * Handles the case when the Resource's metadata has been deleted from
-         * the LogFile.
-         * \return true if resUri was deleted, false otherwsie
-         */
-        bool handleResourceDeletion( const KUrl & resUri );
-
-        /**
-         * Resolve conflicts for properties with multiple cardinalities. It works by locally
-         * making all the changes in the SimpleResource.
-         */
-        void resolveMultipleCardinality( const QList<ChangeLogRecord >& theirRecords, const QList<ChangeLogRecord >& ownRecords );
-        
-        /**
-         * Same as multiple Cardinality resolution, but a lot faster. It figures out which statement
-         * should be present by looking at the max time stamp.
-         */
-        void resolveSingleCardinality( const QList< Nepomuk::ChangeLogRecord >& theirRecords, const QList< Nepomuk::ChangeLogRecord >& ownRecords );
-    };
-
-}
-#endif // CHANGELOGMERGER_H
diff --git a/nepomuk/services/backupsync/service/changelogrecord.cpp b/nepomuk/services/backupsync/service/changelogrecord.cpp
index ae27e14..6fe7f85 100644
--- a/nepomuk/services/backupsync/service/changelogrecord.cpp
+++ b/nepomuk/services/backupsync/service/changelogrecord.cpp
@@ -128,7 +128,10 @@ QString Nepomuk::ChangeLogRecord::toString() const
     QString statement;
     QTextStream ts( &statement );
 
-    Private::serializer->serialize( it, ts, Soprano::SerializationNQuads);
+    if( !Private::serializer->serialize( it, ts, Soprano::SerializationNQuads) ) {
+        kWarning() << "Serialization using NQuads failed for " << d->st;
+        return QString();
+    }
 
     return s + statement;
 }
diff --git a/nepomuk/services/backupsync/service/diffgenerator.cpp b/nepomuk/services/backupsync/service/diffgenerator.cpp
index fdfbba1..9d432db 100644
--- a/nepomuk/services/backupsync/service/diffgenerator.cpp
+++ b/nepomuk/services/backupsync/service/diffgenerator.cpp
@@ -21,7 +21,7 @@
 
 
 #include "diffgenerator.h"
-#include "backupsync.h"
+#include "nrio.h"
 #include "logstorage.h"
 #include "changelog.h"
 
@@ -67,7 +67,7 @@ Nepomuk::DiffGenerator::~DiffGenerator()
 
 
 namespace {
-    const Nepomuk::Types::Property identifyingProperty( Nepomuk::Vocabulary::backupsync::identifyingProperty() );
+    const Nepomuk::Types::Property identifyingProperty( Nepomuk::Vocabulary::NRIO::identifyingProperty() );
 }
 
 bool Nepomuk::DiffGenerator::backupStatement(const Soprano::Statement& st)
diff --git a/nepomuk/services/backupsync/service/identificationset.cpp b/nepomuk/services/backupsync/service/identificationset.cpp
index e811553..9a641ba 100644
--- a/nepomuk/services/backupsync/service/identificationset.cpp
+++ b/nepomuk/services/backupsync/service/identificationset.cpp
@@ -23,7 +23,7 @@
 #include "identificationset.h"
 #include "changelog.h"
 #include "changelogrecord.h"
-#include "backupsync.h"
+#include "nrio.h"
 
 #include <QtCore/QList>
 #include <QtCore/QFile>
@@ -47,15 +47,19 @@
 #include <Nepomuk/ResourceManager>
 
 #include <KDebug>
+#include <Soprano/Vocabulary/NRL>
+
+using namespace Soprano::Vocabulary;
+using namespace Nepomuk::Vocabulary;
 
 namespace {
     class IdentificationSetGenerator {
     public :
         IdentificationSetGenerator( const QSet<QUrl>& uniqueUris, Soprano::Model * m , const QSet<QUrl> & ignoreList = QSet<QUrl>());
 
-        Soprano::Model * model;
-        QSet<QUrl> done;
-        QSet<QUrl> notDone;
+        Soprano::Model * m_model;
+        QSet<QUrl> m_done;
+        QSet<QUrl> m_notDone;
 
         QList<Soprano::Statement> statements;
         
@@ -63,43 +67,46 @@ namespace {
         void iterate();
         QList<Soprano::Statement> generate();
 
-        static const int maxIterationSize = 50;
+        static const int maxIterationSize = 500;
+
+        bool done() const { return m_notDone.isEmpty(); }
     };
 
     IdentificationSetGenerator::IdentificationSetGenerator(const QSet<QUrl>& uniqueUris, Soprano::Model* m, const QSet<QUrl> & ignoreList)
     {
-        notDone = uniqueUris - ignoreList;
-        model = m;
-        done = ignoreList;
-
+        m_notDone = uniqueUris - ignoreList;
+        m_model = m;
+        m_done = ignoreList;
     }
 
     Soprano::QueryResultIterator IdentificationSetGenerator::queryIdentifyingStatements(const QStringList& uris)
     {
+        //
+        // select distinct ?r ?p ?o where { ?r ?p ?o.
+        // ?p rdfs:subPropertyOf nrio:identifyingProperty .
+        // FILTER( ?r in ( <res1>, <res2>, ... ) ) . }
+        //
         QString query = QString::fromLatin1("select distinct ?r ?p ?o where { ?r ?p ?o. "
-                                            "{ ?p %1 %2 .} "
-                                            "UNION { ?p %1 %3. } "
+                                            "?p %1 %2 . "
                                             " FILTER( ?r in ( %4 ) ) . } ")
-                        .arg(Soprano::Node::resourceToN3(Soprano::Vocabulary::RDFS::subPropertyOf()),
-                             Soprano::Node::resourceToN3(Nepomuk::Vocabulary::backupsync::identifyingProperty()),
-                             Soprano::Node::resourceToN3(Soprano::Vocabulary::RDF::type()),
-                             uris.join(", "));
+                        .arg(Soprano::Node::resourceToN3( RDFS::subPropertyOf() ),
+                             Soprano::Node::resourceToN3( NRIO::identifyingProperty() ),
+                             uris.join(", ") );
 
-
-        return model->executeQuery(query, Soprano::Query::QueryLanguageSparql);
+        return m_model->executeQuery(query, Soprano::Query::QueryLanguageSparql);
     }
 
     void IdentificationSetGenerator::iterate()
     {
         QStringList uris;
         
-        QMutableSetIterator<QUrl> iter( notDone );
+        QMutableSetIterator<QUrl> iter( m_notDone );
         while( iter.hasNext() ) {
             const QUrl & uri = iter.next();
-            done.insert( uri );
+            m_done.insert( uri );
             
             uris.append( Soprano::Node::resourceToN3( uri ) );
-            
+
             iter.remove();
             if( uris.size() == maxIterationSize )
                 break;
@@ -116,8 +123,8 @@ namespace {
             // If the object is also a nepomuk uri, it too needs to be identified.
             const QUrl & objUri = obj.uri();
             if( objUri.toString().startsWith("nepomuk:/res/") ) {
-                if( !done.contains( objUri ) ) {
-                    notDone.insert( objUri );
+                if( !m_done.contains( objUri ) ) {
+                    m_notDone.insert( objUri );
                 }
             }
         }
@@ -125,9 +132,9 @@ namespace {
 
     QList<Soprano::Statement> IdentificationSetGenerator::generate()
     {
-        done.clear();
+        m_done.clear();
 
-        while( !notDone.isEmpty() ) {
+        while( !done() ) {
             iterate();
         }
         return statements;
@@ -200,7 +207,7 @@ Nepomuk::IdentificationSet Nepomuk::IdentificationSet::fromTextStream(QTextStrea
 namespace {
 
     //
-    // Seperate all the unique URIs of scheme "nepomuk" from the subject and object in all the statements.
+    // Separate all the unique URIs of scheme "nepomuk" from the subject and object in all the statements.
     //
     // vHanda: Maybe we should separate the graphs as well. Identification isn't meant for graphs.
     QSet<QUrl> getUniqueUris( const QList<Nepomuk::ChangeLogRecord> records ) {
@@ -259,7 +266,7 @@ namespace {
         QString query = QString::fromLatin1( "ask { %1 %2 %3 }" )
         .arg( Soprano::Node::resourceToN3( prop ) )
         .arg( Soprano::Node::resourceToN3(Soprano::Vocabulary::RDFS::subPropertyOf()) )
-        .arg( Soprano::Node::resourceToN3(Nepomuk::Vocabulary::backupsync::identifyingProperty()) );
+        .arg( Soprano::Node::resourceToN3(Nepomuk::Vocabulary::NRIO::identifyingProperty()) );
         return model->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue();
     }
 }
@@ -346,3 +353,30 @@ void Nepomuk::IdentificationSet::mergeWith(const IdentificationSet & rhs)
     return;
 }
 
+void Nepomuk::IdentificationSet::createIdentificationSet(const QSet<QUrl>& uniqueUris, const QUrl& outputUrl)
+{
+    QFile file( outputUrl.path() );
+    if( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) ) {
+        kWarning() << "File could not be opened : " << outputUrl.path();
+        return;
+    }
+    
+    QTextStream out( &file );
+
+    IdentificationSet set;
+    Soprano::Model * model = ResourceManager::instance()->mainModel();
+    
+    IdentificationSetGenerator generator( uniqueUris, model );
+    while( !generator.done() ) {
+        generator.statements.clear();
+        kDebug() << "iterating";
+        generator.iterate();
+        kDebug() << "Done : " << generator.m_done.size();
+        kDebug() << "Num statements: " << generator.statements.size();
+        set.d->m_statements.clear();
+        set.d->m_statements = generator.statements;
+        set.save( out );
+    }
+    kDebug() << "Done creating Identification Set";
+}
+
diff --git a/nepomuk/services/backupsync/service/identificationset.h b/nepomuk/services/backupsync/service/identificationset.h
index 854ab51..8757e2e 100644
--- a/nepomuk/services/backupsync/service/identificationset.h
+++ b/nepomuk/services/backupsync/service/identificationset.h
@@ -99,6 +99,8 @@ namespace Nepomuk {
             
             IdentificationSet & operator=( const IdentificationSet & rhs );
             IdentificationSet& operator<<(const IdentificationSet & rhs);
+
+            static void createIdentificationSet( const QSet< QUrl >& uniqueUris, const QUrl& outputUrl );
         private :
             class Private;
             QSharedDataPointer<Private> d;
diff --git a/nepomuk/services/backupsync/service/identifier.cpp b/nepomuk/services/backupsync/service/identifier.cpp
deleted file mode 100644
index 5b4c0f3..0000000
--- a/nepomuk/services/backupsync/service/identifier.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "changelog.h"
-#include "identifieradaptor.h"
-
-#include <QtDBus/QDBusConnection>
-
-#include <QtCore/QMutexLocker>
-#include <QtCore/QHashIterator>
-#include <QtCore/QFile>
-
-#include <Soprano/Model>
-#include <Soprano/Graph>
-#include <Soprano/QueryResultIterator>
-#include <Soprano/Vocabulary/RDF>
-#include <Soprano/Serializer>
-#include <Soprano/PluginManager>
-#include <Soprano/Util/SimpleStatementIterator>
-
-#include <Nepomuk/ResourceManager>
-#include <Nepomuk/Resource>
-#include <Nepomuk/Variant>
-#include <Nepomuk/Vocabulary/NIE>
-
-#include <KDebug>
-
-Nepomuk::Identifier::Identifier(QObject* parent): QThread(parent)
-{
-    //Register DBus interface
-    new IdentifierAdaptor( this );
-    QDBusConnection dbus = QDBusConnection::sessionBus();
-    dbus.registerObject( QLatin1String("/identifier"), this );
-
-    start();
-}
-
-
-Nepomuk::Identifier::~Identifier()
-{
-    stop();
-    quit();
-}
-
-
-int Nepomuk::Identifier::process(const Nepomuk::SyncFile& sf)
-{
-    m_queueMutex.lock();
-
-    SyncFileIdentifier* identifier = new SyncFileIdentifier( sf );
-    int id = identifier->id();
-    m_queue.enqueue( identifier );
-
-    m_queueMutex.unlock();
-    m_queueWaiter.wakeAll();
-
-    kDebug() << "Processing ID : " << id;
-    return id;
-}
-
-
-void Nepomuk::Identifier::stop()
-{
-    m_stopped = true;
-    m_queueWaiter.wakeAll();
-}
-
-
-
-void Nepomuk::Identifier::run()
-{
-    m_stopped = false;
-
-    while( !m_stopped ) {
-
-        // lock for initial iteration
-        m_queueMutex.lock();
-
-        while( !m_queue.isEmpty() ) {
-
-            SyncFileIdentifier* identifier = m_queue.dequeue();
-
-            // unlock after queue utilization
-            m_queueMutex.unlock();
-
-            identifier->load();
-            identifyAllWithCompletedSignals( identifier );
-
-            emit identificationDone( identifier->id(), identifier->unidentified().size() );
-            
-            m_processMutex.lock();
-            m_processes[ identifier->id() ] = identifier;
-            m_processMutex.unlock();
-
-            // Send the sigals
-            foreach( const KUrl & uri, identifier->unidentified() ) {
-                emitNotIdentified( identifier->id(), identifier->statements( uri ).toList() );
-            }
-
-            foreach( const KUrl & uri, identifier->mappings().uniqueKeys() ) {
-                emit identified( identifier->id(), uri.url(), identifier->mappedUri( uri ).url() );
-            }
-
-//             if( identifier->allIdentified() ) {
-//                 m_processes.remove( identifier->id() );
-//                 delete identifier;
-//             }
-
-            m_queueMutex.lock();
-        }
-
-        // wait for more input
-        kDebug() << "Waiting...";
-        m_queueWaiter.wait( &m_queueMutex );
-        m_queueMutex.unlock();
-        kDebug() << "Woke up.";
-    }
-}
-
-
-bool Nepomuk::Identifier::identify(int id, const QString& oldUriString, const QString& newUriString)
-{
-    QUrl oldUri( oldUriString );
-    QUrl newUri( newUriString );
-    
-    kDebug() << newUri;
-    // Lock the mutex and all
-    QMutexLocker lock ( &m_processMutex );
-
-    QHash<int, SyncFileIdentifier*>::iterator it = m_processes.find( id );
-    if( it == m_processes.end() )
-        return false;
-
-    SyncFileIdentifier* ip = *it;
-
-    if ( oldUri.scheme() != QLatin1String("nepomuk") )
-        return false;
-
-    if( newUri.scheme() == QLatin1String("nepomuk") ) {
-        ip->forceResource( oldUri, Nepomuk::Resource(newUri) );
-    }
-    else if( newUri.scheme() == "file" ) {
-        ip->forceResource( oldUri, Nepomuk::Resource(newUri) );
-    }
-
-    m_queueMutex.lock();
-    m_queue.enqueue( ip );
-    m_queueMutex.unlock();
-    m_queueWaiter.wakeAll();
-
-    return true;
-}
-
-
-bool Nepomuk::Identifier::ignore(int id, const QString& urlString, bool ignoreSub)
-{
-    KUrl url( urlString );
-    // Lock the mutex and all
-    QMutexLocker lock ( &m_processMutex );
-
-    QHash<int, SyncFileIdentifier*>::iterator it = m_processes.find( id );
-    if( it == m_processes.end() )
-        return false;
-
-    SyncFileIdentifier* identifier = *it;
-    return identifier->ignore( url, ignoreSub );
-}
-
-void Nepomuk::Identifier::ignoreAll(int id)
-{
-    QMutexLocker lock ( &m_processMutex );
-    
-    QHash<int, SyncFileIdentifier*>::iterator it = m_processes.find( id );
-    if( it == m_processes.end() )
-        return;
-    
-    SyncFileIdentifier* identifier = *it;
-    foreach( const KUrl & url, identifier->unidentified() ) {
-        identifier->ignore( url, true );
-    }
-}
-
-void Nepomuk::Identifier::emitNotIdentified(int id, const QList< Soprano::Statement >& stList)
-{
-    const Soprano::Serializer* serializer = Soprano::PluginManager::instance()->discoverSerializerForSerialization( Soprano::SerializationNQuads );
-    
-    Soprano::Util::SimpleStatementIterator it( stList );
-    QString ser;
-    QTextStream stream( &ser );
-    serializer->serialize( it, stream, Soprano::SerializationNQuads );
-    
-    emit notIdentified( id, ser );
-}
-
-void Nepomuk::Identifier::test()
-{
-    kDebug() << "Test!";
-}
-
-void Nepomuk::Identifier::completeIdentification(int id)
-{
-    kDebug() << id;
-    
-    QMutexLocker lock ( &m_processMutex );
-    
-    QHash<int, SyncFileIdentifier*>::iterator it = m_processes.find( id );
-    if( it == m_processes.end() )
-        return;
-    
-    SyncFileIdentifier* identifier = *it;
-    m_processes.remove( id );
-    
-    ChangeLog log = identifier->convertedChangeLog();
-    kDebug() << "ChangeLog of size " << log.size() << " has been converted";
-    if( !log.empty() ) {
-        kDebug() << "sending ChangeLog of size : " << log.size();
-        emit processed( log );
-    }
-
-    delete identifier;
-}
-
-
-void Nepomuk::Identifier::identifyAllWithCompletedSignals(Nepomuk::SyncFileIdentifier* ident)
-{
-    int unidentified = ident->unidentified().size();
-    float step = 100.0/unidentified;
-    float progress = 0;
-
-    emit completed( ident->id(), 0 );
-    foreach( const KUrl & url, ident->unidentified() ) {
-        ident->identify( url );
-
-        progress += step;
-        emit completed( ident->id(), (int)progress );
-    }
-    emit completed( ident->id(), 100 );
-}
-
-
-#include "identifier.moc"
diff --git a/nepomuk/services/backupsync/service/identifier.h b/nepomuk/services/backupsync/service/identifier.h
deleted file mode 100644
index 8f22f93..0000000
--- a/nepomuk/services/backupsync/service/identifier.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef IDENTIFICATIONTHREAD_H
-#define IDENTIFICATIONTHREAD_H
-
-#include <QtCore/QThread>
-#include <QtCore/QQueue>
-#include <QtCore/QMutex>
-#include <QtCore/QWaitCondition>
-#include <QtCore/QQueue>
-#include <QtCore/QUrl>
-
-#include "syncfileidentifier.h"
-
-namespace Soprano {
-    class Model;
-}
-
-namespace Nepomuk {
-
-    class SyncFile;
-    class ChangeLog;
-    
-    class Identifier : public QThread
-    {
-        Q_OBJECT
-
-    public :
-        Identifier( QObject* parent = 0 );
-        virtual ~Identifier();
-
-        void stop();
-        void test();
-
-    Q_SIGNALS:
-        void identified( int id, const QString & oldUri, const QString & newUri );
-        void notIdentified( int id, const QString & serializedStatements );
-
-        void identificationDone( int id, int unidentified );
-        void processed( const Nepomuk::ChangeLog & logFile );
-        
-        void completed( int id, int progress );
-    public Q_SLOTS:
-        int process( const SyncFile & sf );
-
-        /**
-         * Add the (oldUri, newUri) pair to the identifier list
-         * @param oldUri has to be a nepomuk:/res/
-         * @param newUri can be a nepomuk:/res/ or file:/
-         */
-        bool identify( int id, const QString & oldUri, const QString & newUri );
-
-        bool ignore(int id, const QString& url, bool ignoreSubDirectories);
-
-        void ignoreAll( int id );
-
-        void completeIdentification( int id );
-
-    protected:
-        virtual void run();
-
-    private :
-        QQueue<SyncFileIdentifier *> m_queue;
-        QMutex m_queueMutex;
-        QWaitCondition m_queueWaiter;
-
-        bool m_stopped;
-
-        QHash< int, SyncFileIdentifier *> m_processes;
-        QMutex m_processMutex;
-
-        void emitNotIdentified( int id, const QList<Soprano::Statement> & stList );
-        void identifyAllWithCompletedSignals( SyncFileIdentifier * ident );
-    };
-
-}
-#endif // IDENTIFICATIONTHREAD_H
diff --git a/nepomuk/services/backupsync/service/logstorage.cpp b/nepomuk/services/backupsync/service/logstorage.cpp
index 21ec4cf..249b995 100644
--- a/nepomuk/services/backupsync/service/logstorage.cpp
+++ b/nepomuk/services/backupsync/service/logstorage.cpp
@@ -83,7 +83,7 @@ Nepomuk::ChangeLog Nepomuk::LogStorage::getChangeLog(const QDateTime& min)
             continue;
 
         //TODO: Optimize : Every record shouldn't be checked. Be smart!
-        log += ChangeLog::fromUrl( m_dirUrl + fileName, min );
+        log += ChangeLog::fromUrl( QString( m_dirUrl + fileName ), min );
     }
 
     //TODO: Optimize this!
@@ -109,7 +109,7 @@ void Nepomuk::LogStorage::addRecord(const Nepomuk::ChangeLogRecord& record)
 
 //IMPORTANT: This function doesn't actually remove ALL the records less than min
 // This has been done purposely, as otherwise one would need to read the entire contents
-// of atleast one LogFile.
+// of at least one LogFile.
 void Nepomuk::LogStorage::removeRecords(const QDateTime& min)
 {
     QDir dir( m_dirUrl );
diff --git a/nepomuk/services/backupsync/service/merger.cpp b/nepomuk/services/backupsync/service/merger.cpp
deleted file mode 100644
index 3fcaa7c..0000000
--- a/nepomuk/services/backupsync/service/merger.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "merger.h"
-
-#include "changelog.h"
-#include "syncfile.h"
-#include "mergeradaptor.h"
-
-#include <QtDBus/QDBusConnection>
-
-#include <KDebug>
-
-Nepomuk::Merger::Merger( QObject* parent )
-    : QThread( parent )
-{
-    //Register DBus interface
-    new MergerAdaptor( this );
-    QDBusConnection dbus = QDBusConnection::sessionBus();
-    dbus.registerObject( QLatin1String("/merger"), this );
-    
-    start();
-}
-
-
-Nepomuk::Merger::~Merger()
-{
-    stop();
-    wait();
-}
-
-
-void Nepomuk::Merger::stop()
-{
-    m_stopped = true;
-    m_queueWaiter.wakeAll();
-}
-
-
-int Nepomuk::Merger::process(const Nepomuk::ChangeLog& changeLog )
-{
-    kDebug();
-    m_queueMutex.lock();
-
-    kDebug() << "Received ChangeLog -- " << changeLog.size();
-    ChangeLogMerger * request = new ChangeLogMerger( changeLog );
-    m_queue.enqueue( request );
-
-    m_queueMutex.unlock();
-    m_queueWaiter.wakeAll();
-
-    return request->id();
-}
-
-
-void Nepomuk::Merger::run()
-{
-    m_stopped = false;
-
-    while( !m_stopped ) {
-
-        // lock for initial iteration
-        m_queueMutex.lock();
-
-        while( !m_queue.isEmpty() ) {
-
-            ChangeLogMerger* request = m_queue.dequeue();
-            //kDebug() << "Processing request #" << request->id() << " with size " << request->size();
-
-            // unlock after queue utilization
-            m_queueMutex.unlock();
-
-            //FIXME: Fake completed signals!
-            emit completed( 5 );
-            request->load();
-            emit completed( 55 );
-            request->mergeChangeLog();
-            emit completed( 100 );
-            
-            /*
-            if( !request->done() ) {
-                QMutexLocker lock( &m_processMutex );
-                m_processes[ request->id() ] = request;
-            }
-
-            disconnect( request, SIGNAL(completed(int)),
-                        this, SIGNAL(completed(int)) );
-            disconnect( request, SIGNAL(multipleMerge(QString,QString)),
-                     this, SIGNAL(multipleMerge(QString,QString)) );
-
-            if( request->done() ){*/
-
-            foreach( const Soprano::Statement & st, request->multipleMergers() ) {
-                emit multipleMerge( st.subject().uri().toString(),
-                                    st.predicate().uri().toString() );
-            }
-            
-            m_processes.remove( request->id() );
-            delete request;
-
-            m_queueMutex.lock();
-        }
-
-        kDebug() << "Waiting...";
-        m_queueWaiter.wait( &m_queueMutex );
-        m_queueMutex.unlock();
-        kDebug() << "Woke up.";
-    }
-}
-
-
-#include "merger.moc"
diff --git a/nepomuk/services/backupsync/service/merger.h b/nepomuk/services/backupsync/service/merger.h
deleted file mode 100644
index feb20a5..0000000
--- a/nepomuk/services/backupsync/service/merger.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef MERGETHREAD_H
-#define MERGETHREAD_H
-
-#include <QtCore/QThread>
-#include <QtCore/QMutex>
-#include <QtCore/QWaitCondition>
-#include <QtCore/QUrl>
-#include <QtCore/QQueue>
-
-#include "changelogmerger.h"
-
-namespace Soprano {
-    class Model;
-}
-
-namespace Nepomuk {
-    
-    class ResourceManager;
-    class ChangeLog;
-
-    class Merger : public QThread
-    {
-        Q_OBJECT
-
-    public :
-        Merger( QObject* parent = 0 );
-        virtual ~Merger();
-
-        void stop();
-
-    public Q_SLOTS:
-        int process( const Nepomuk::ChangeLog & logFile );
-
-    Q_SIGNALS:
-        void completed( int percent );
-        void multipleMerge( const QString & uri, const QString & prop );
-
-    protected:
-        virtual void run();
-
-    private:
-        QQueue<ChangeLogMerger*> m_queue;
-        QMutex m_queueMutex;
-        QWaitCondition m_queueWaiter;
-
-        bool m_stopped;
-
-        QHash<int, ChangeLogMerger*> m_processes;
-        QMutex m_processMutex;
-    };
-
-}
-#endif // MERGETHREAD_H
diff --git a/nepomuk/services/backupsync/service/nepomukbackupsync.desktop b/nepomuk/services/backupsync/service/nepomukbackupsync.desktop
index 297b893..2562bb3 100644
--- a/nepomuk/services/backupsync/service/nepomukbackupsync.desktop
+++ b/nepomuk/services/backupsync/service/nepomukbackupsync.desktop
@@ -7,6 +7,7 @@ X-KDE-Nepomuk-start-on-demand=false
 X-KDE-Nepomuk-dependencies=nepomukstorage
 Name=Nepomuk Backup and Sync
 Name[bg]=Модул за архивиране и синхронизиране Nepomuk
+Name[bs]=Rezerva i sinhronizacija Nepomuka
 Name[ca]=Còpia de seguretat i sincronització del Nepomuk
 Name[ca@valencia]=Còpia de seguretat i sincronització del Nepomuk
 Name[cs]=Zálohování a synchronizace Nepomuku
@@ -23,6 +24,7 @@ Name[he]=Nepomuk גיבויים וסינכרונים
 Name[hr]=Nepomukova sigurnosna kopija i sinkronizacija
 Name[hu]=Nepomuk mentés és szinkronizálás
 Name[ia]=Retrocopia (Backup) e Sync de Nepomuk
+Name[is]=Nepomuk öryggisafritun og samstilling
 Name[it]=Copia di sicurezza e sincronizzazione di Nepomuk
 Name[ja]=Nepomuk バックアップと同期
 Name[kk]=Nepomuk сақтық көшірмелеу және қадамдастыру
@@ -30,9 +32,11 @@ Name[km]=ការ​ធ្វើ​សមកាលកម្ម និង​ប
 Name[kn]=ನೆಪೋಮುಕ್ ಬ್ಯಾಕ್‌ಅಪ್ ಹಾಗು ಸಿಂಕ್
 Name[ko]=Nepomuk 백업 및 동기화
 Name[lt]=Nepomuk atsarginė kopija ir sinchronizacija
+Name[lv]=Nepomuk rezerves kopiju veidošana un sinhronizācija
 Name[nb]=Nepomuk sikringskopi og synkronisering
 Name[nds]=Nepomuk-Sekerheitkopie un Synkroniseren
 Name[nl]=Nepomuk reservekopie-synchronisatie
+Name[nn]=Reservekopi og synkronisering av Nepomuk
 Name[pa]=ਨਿਪੋਮੁਕ ਬੈਕਅੱਪ ਅਤੇ ਸਿੰਕਨਿਪੋਮੁਕ ਬੈਕਅਨਿਪੋਮੁਕ ਬੈਕਅੱਤ
 Name[pl]=Kopia zapasowa i synchronizacja Nepomuka
 Name[pt]=Salvaguarda e Sincronização do Nepomuk
@@ -48,11 +52,14 @@ Name[sr@latin]=Rezerva i sinhronizacija Nepomuka
 Name[sv]=Nepomuk säkerhetskopiering och synkronisering
 Name[th]=การสำรองและปรับเทียบข้อมูลของ Nepomuk
 Name[tr]=Nepomuk Yedekleme ve Eşzamanlama
+Name[ug]=Nepomuk زاپاسلاش ۋە قەدەمداش
 Name[uk]=Створення резервних копій і синхронізація Nepomuk
 Name[x-test]=xxNepomuk Backup and Syncxx
+Name[zh_CN]=Nepomuk 备份和同步
 Name[zh_TW]=Nepomuk 備份與同步
 Comment=Nepomuk Service which handles backup and sync.
 Comment[bg]=Услуга на Nepomuk, която се грижи за архивиране и синхронизиране
+Comment[bs]=Servis Nepomuka za pravljenje rezervi i sinhronizaciju.
 Comment[ca]=Servei del Nepomuk que gestiona còpies de seguretat i sincronització.
 Comment[ca@valencia]=Servei del Nepomuk que gestiona còpies de seguretat i sincronització.
 Comment[cs]=Služba Nepomuku pro zálohu a synchronizaci.
@@ -69,15 +76,18 @@ Comment[he]=שירות של Nepomuk המטפל בגיבוי וסינכרון.
 Comment[hr]=Nepomukov servis koji upravlja sigurnosnim kopijama i sinkronizacijom.
 Comment[hu]=Biztonsági mentéseket és szinkronizálást kezelő Nepomuk szolgáltatás.
 Comment[ia]=Servicio de Nepomuk que manea retrocopia e sync.
+Comment[is]=Nepomuk þjónusta sem sér um öryggisafritun og samstillingu
 Comment[it]=Servizio di Nepomuk che gestisce le copie di sicurezza e la sincronizzazione.
 Comment[ja]=バックアップと同期を行う Nepomuk サービス
 Comment[kk]=Cақтық көшірмелеу және қадамдастыруды басқаратын Nepomuk қызметі.
 Comment[km]=សេវា Nepomuk ដែល​គ្រប់គ្រង​ការ​ធ្វើ​សមកាលកម្ម និង​បម្រុង​ទុក
 Comment[ko]=백업과 동기화를 담당하는 Nepomuk 서비스입니다.
 Comment[lt]=Nepamok tarnyba, valdanti atsargines kopijas ir sinchronizavimą
+Comment[lv]=Nepomuk serviss, kurš vada rezerves kopiju veidošanu un sinhronizāciju.
 Comment[nb]=Nepomuk-tjeneste som håndterer sikringskopi og synkronisering.
 Comment[nds]=Nepomuk-Deenst för Sekerheitkopien un Synkroniseren.
 Comment[nl]=Nepomuk service die reservekopie-synchronisatie afhandelt.
+Comment[nn]=Nepomuk-teneste som handterer reservekopiering og synkronisering.
 Comment[pa]=ਨਿਪੋਮੁਕ ਸਰਵਿਸ, ਜੋ ਕਿ ਬੈਕਅੱਪ ਤੇ ਸਿੰਕ ਨੂੰ ਹੈਂਡਸ ਕਰਦੀ ਹੈ।
 Comment[pl]=Usługa Nepomuka, która zajmuje się kopiami zapasowymi i synchronizacją.
 Comment[pt]=O serviço do Nepomuk que lida com a salvaguarda e a sincronização dos dados.
@@ -93,6 +103,8 @@ Comment[sr@latin]=Servis Nepomuka za pravljenje rezervi i sinhronizaciju.
 Comment[sv]=Nepomuk-tjänst som hanterar säkerhetskopiering och synkronisering.
 Comment[th]=บริการ Nepomuk ที่ทำงานเกี่ยวกับการสำรองและปรับเทียบข้อมูล
 Comment[tr]=Yedekleme ve eşzamanlama yapan Nepomuk servisi
+Comment[ug]=زاپاسلاش ۋە قەدەمداشنى بىر تەرەپ قىلىدىغان Nepomuk مۇلازىمىتى
 Comment[uk]=Служба Nepomuk, призначена для створення резервних копій і синхронізації.
 Comment[x-test]=xxNepomuk Service which handles backup and sync.xx
+Comment[zh_CN]=Nepomuk 处理备份和同步的服务。
 Comment[zh_TW]=處理備份與同步的 Nepomuk 服務。
diff --git a/nepomuk/services/backupsync/service/resourcelog.cpp b/nepomuk/services/backupsync/service/resourcelog.cpp
deleted file mode 100644
index a24d3f0..0000000
--- a/nepomuk/services/backupsync/service/resourcelog.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "resourcelog.h"
-#include "changelog.h"
-#include "changelogrecord.h"
-
-#include <QtCore/QMultiHash>
-
-#include <Nepomuk/Types/Property>
-#include <algorithm>
-
-
-Nepomuk::ResourceLogMap Nepomuk::ResourceLogMap::fromChangeLogRecordList(const QList<ChangeLogRecord>& records)
-{
-    ResourceLogMap hash;
-
-    //
-    // Organize according to resource uri
-    //
-    QMultiHash<KUrl, ChangeLogRecord> multiHash;
-    foreach( const ChangeLogRecord &r, records ) {
-        multiHash.insert( r.st().subject().uri(), r );
-    }
-
-    //
-    // Convert to MergeHash
-    //
-    const QList<KUrl> & keys = multiHash.uniqueKeys();
-    foreach( const KUrl & key, keys ) {
-        ResourceLog ms;
-        ms.uri = key;
-
-        const QList<ChangeLogRecord>& records = multiHash.values( key );
-        foreach( const ChangeLogRecord & r, records ) {
-            const KUrl &pred = r.st().predicate().uri();
-            ms.prop.insert( pred, r );
-        }
-        hash.insert( ms.uri, ms );
-    }
-
-    return hash;
-}
-
-
-Nepomuk::ResourceLogMap Nepomuk::ResourceLogMap::fromChangeLog(const Nepomuk::ChangeLog& log)
-{
-    return fromChangeLogRecordList( log.toList() );
-}
-
-
-namespace {
-
-    Nepomuk::ChangeLogRecord maxRecord( const QList<Nepomuk::ChangeLogRecord> & records ) {
-        QList<Nepomuk::ChangeLogRecord>::const_iterator it = std::max_element( records.begin(), records.end() );
-        if( it != records.constEnd() )
-            return *it;
-        return Nepomuk::ChangeLogRecord();
-    }
-
-    typedef QHash<Soprano::Node, Nepomuk::ChangeLogRecord> OptimizeHash;
-
-}
-
-
-void Nepomuk::ResourceLogMap::optimize()
-{
-    QMutableHashIterator<KUrl, ResourceLog> it( *this );
-    while( it.hasNext() ) {
-        it.next();
-
-        ResourceLog & log = it.value();
-
-        const QList<KUrl> & properties = log.prop.uniqueKeys();
-        foreach( const KUrl & propUri, properties ) {
-            QList<ChangeLogRecord> records = log.prop.values( propUri );
-
-            Types::Property property( propUri );
-            int maxCard = property.maxCardinality();
-
-            if( maxCard == 1 ) {
-                ChangeLogRecord max = maxRecord( records );
-                records.clear();
-                records.append( max );
-            }
-            else {
-                // The records have to be sorted by timeStamp in order to optimize them
-                qSort( records );
-
-                OptimizeHash hash;
-                foreach( const ChangeLogRecord & record, records ) {
-                    if( record.added() ) {
-                        OptimizeHash::const_iterator iter = hash.constFind( record.st().object() );
-                        if( iter != hash.constEnd() ){
-                            if( !iter.value().added() ) {
-                                hash.remove( record.st().object() );
-                            }
-                        }
-                        else {
-                            hash.insert( record.st().object(), record );
-                        }
-                    }
-                    else {
-                        OptimizeHash::const_iterator iter = hash.constFind( record.st().object() );
-                        if( iter != hash.constEnd() ) {
-                            if( iter.value().added() ) {
-                                hash.remove( record.st().object() );
-                            }
-                        }
-                        else
-                            hash.insert( record.st().object(), record );
-                    }
-                }
-                records = hash.values();
-                qSort( records );
-
-                // Update the log
-                log.prop.remove( propUri );
-                foreach( const ChangeLogRecord & record, records )
-                    log.prop.insert( propUri, record );
-            }
-        }
-
-    }
-}
diff --git a/nepomuk/services/backupsync/service/resourcelog.h b/nepomuk/services/backupsync/service/resourcelog.h
deleted file mode 100644
index fde6dc5..0000000
--- a/nepomuk/services/backupsync/service/resourcelog.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-#ifndef RESOURCELOG_H
-#define RESOURCELOG_H
-
-#include <QtCore/QUrl>
-#include <QtCore/QMultiHash>
-
-#include <KUrl>
-
-#include "changelog.h"
-#include "changelogrecord.h"
-
-namespace Nepomuk {
-
-        
-        class ResourceLog
-        {
-        public:
-            KUrl uri;
-            QMultiHash<KUrl, ChangeLogRecord> prop;
-        };
-
-        class ResourceLogMap : public QHash<KUrl, ResourceLog> {
-        public:
-            static ResourceLogMap fromChangeLogRecordList( const QList<ChangeLogRecord> & records );
-            static ResourceLogMap fromChangeLog( const Nepomuk::ChangeLog& log );
-
-            void optimize();
-        };
-    
-}
-#endif // RESOURCELOG_H
diff --git a/nepomuk/services/backupsync/service/syncfile.cpp b/nepomuk/services/backupsync/service/syncfile.cpp
index c0a08d7..a0d3e66 100644
--- a/nepomuk/services/backupsync/service/syncfile.cpp
+++ b/nepomuk/services/backupsync/service/syncfile.cpp
@@ -129,6 +129,7 @@ bool Nepomuk::SyncFile::load(const QUrl& syncFile)
 
 bool Nepomuk::SyncFile::save( const QUrl& outFile )
 {
+    kDebug() << "Saving at " << outFile;
     KTempDir tempDir;
 
     QUrl logFileUrl( tempDir.name() + "changelog" );
@@ -137,15 +138,21 @@ bool Nepomuk::SyncFile::save( const QUrl& outFile )
     QUrl identFileUrl( tempDir.name() + "identificationset" );
     d->m_identificationSet.save( identFileUrl );
 
+    return createSyncFile( logFileUrl, identFileUrl, outFile );
+}
+
+// static
+bool Nepomuk::SyncFile::createSyncFile(const QUrl& logfile, const QUrl& identFile, const QUrl & outFile)
+{
     KTar tarFile( outFile.toString(), QString::fromLatin1("application/x-gzip") );
     if( !tarFile.open( QIODevice::WriteOnly ) ) {
         kWarning() << "File could not be opened : " << outFile.path();
         return false;
     }
-
-    tarFile.addLocalFile( logFileUrl.path(), "changelog" );
-    tarFile.addLocalFile( identFileUrl.path(), "identificationset" );
-
+    
+    tarFile.addLocalFile( logfile.path(), "changelog" );
+    tarFile.addLocalFile( identFile.path(), "identificationset" );
+    
     return true;
 }
 
diff --git a/nepomuk/services/backupsync/service/syncfile.h b/nepomuk/services/backupsync/service/syncfile.h
index 49f58fe..4363c7c 100644
--- a/nepomuk/services/backupsync/service/syncfile.h
+++ b/nepomuk/services/backupsync/service/syncfile.h
@@ -115,6 +115,7 @@ namespace Nepomuk {
         const ChangeLog& changeLog() const;
         const IdentificationSet & identificationSet() const;
 
+        static bool createSyncFile( const QUrl& logfile, const QUrl& identFile, const QUrl& outFile );
     private:
         class Private;
         Private * d;
diff --git a/nepomuk/services/backupsync/service/syncfileidentifier.cpp b/nepomuk/services/backupsync/service/syncfileidentifier.cpp
deleted file mode 100644
index a8a60f8..0000000
--- a/nepomuk/services/backupsync/service/syncfileidentifier.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "syncfileidentifier.h"
-#include "changelogrecord.h"
-#include "simpleresource.h"
-
-#include <QtCore/QFile>
-#include <QtCore/QDir>
-
-#include <KDebug>
-
-#include <Nepomuk/Resource>
-#include <Nepomuk/Variant>
-#include <Nepomuk/Vocabulary/NIE>
-
-int Nepomuk::SyncFileIdentifier::NextId = 0;
-
-Nepomuk::SyncFileIdentifier::SyncFileIdentifier(const Nepomuk::SyncFile& sf)
-    : ResourceIdentifier()
-{
-    m_id = NextId++;
-
-    m_changeLog = sf.changeLog();
-    m_identificationSet = sf.identificationSet();
-
-    //kDebug() << "Identification Set - ";
-    //kDebug() << m_identificationSet.toList();
-}
-
-Nepomuk::SyncFileIdentifier::~SyncFileIdentifier()
-{
-    // What do we do over here?
-}
-
-namespace {
-    
-    //
-    // Removes the old home directory and replaces it with the current one
-    // TODO: Make it OS independent
-    //
-    QUrl translateHomeUri( const QUrl & uri ) {
-        QString uriString = uri.toString();
-        
-        QRegExp regEx("^file://(/home/[^/]*)(/.*)$");
-        if( regEx.exactMatch( uriString ) ) {
-            QString newUriString = "file://" + QDir::homePath() + regEx.cap(2);
-            
-            uriString.replace( regEx, newUriString );
-            return QUrl( newUriString );
-        }
-        return uri;
-    }
-}
-
-void Nepomuk::SyncFileIdentifier::load()
-{
-    if( unidentified().size() > 0 )
-        return;
-
-    //
-    // Translate all file:// uri so that the home dir is correct.
-    //
-    QList<Soprano::Statement> identList = m_identificationSet.toList();
-    QMutableListIterator<Soprano::Statement> it( identList );
-    while( it.hasNext() ) {
-        it.next();
-
-        Soprano::Statement & st = it.value();
-        if( st.object().isResource() && st.object().uri().scheme() == QLatin1String("file") )
-            st.setObject( Soprano::Node( translateHomeUri( st.object().uri() ) ) );
-    }
-
-    //kDebug() << "After translation : ";
-    //kDebug() << identList;
-    
-    addStatements( identList );
-}
-
-
-Nepomuk::ChangeLog Nepomuk::SyncFileIdentifier::convertedChangeLog()
-{
-    QList<ChangeLogRecord> masterLogRecords = m_changeLog.toList();
-    kDebug() << "masterLogRecords : " << masterLogRecords.size();
-    
-    QList<ChangeLogRecord> identifiedRecords;
-    QMutableListIterator<ChangeLogRecord> it( masterLogRecords );
-    
-    while( it.hasNext() ) {
-        ChangeLogRecord r = it.next();
-        
-        // Identify Subject
-        KUrl subUri = r.st().subject().uri();
-        if( subUri.scheme() == QLatin1String("nepomuk") ) {
-            KUrl newUri = mappedUri( subUri );
-            if( newUri.isEmpty() )
-                continue;
-
-            r.setSubject( newUri );
-        }
-        
-        // Identify object
-        if( r.st().object().isResource() ) {
-            KUrl objUri = r.st().object().uri();
-            if( objUri.scheme() == QLatin1String("nepomuk") ) {
-                KUrl newUri = mappedUri( objUri );
-                if( newUri.isEmpty() )
-                    continue;
-
-                r.setObject( newUri );
-            }
-        }
-        
-        identifiedRecords.push_back( r );
-        
-        // Remove the statement from the masterchangerecords
-        it.remove();
-    }
-    
-    // Update the master change log
-    m_changeLog = ChangeLog::fromList( masterLogRecords );
-    
-    return ChangeLog::fromList( identifiedRecords );
-}
-
-void Nepomuk::SyncFileIdentifier::identifyAll()
-{
-    Nepomuk::Sync::ResourceIdentifier::identifyAll();
-}
-
-int Nepomuk::SyncFileIdentifier::id()
-{
-    return m_id;
-}
-
-
-Nepomuk::Resource Nepomuk::SyncFileIdentifier::createNewResource(const Sync::SimpleResource & simpleRes) const
-{
-    kDebug();
-    Nepomuk::Resource res;
-    
-    if( simpleRes.isFileDataObject() ) {
-        res = Nepomuk::Resource( simpleRes.nieUrl() );
-        if( res.exists() ) {
-            // If the resource already exists. We should not create it. This is to avoid the bug where
-            // a different file with the same nie:url exists. If it was the same file, identification
-            // should have found it. If it hasn't, well tough luck. No other option but to manually
-            // identify
-            return Resource();
-        }
-    }
-    
-    const QList<KUrl> & keys = simpleRes.uniqueKeys();
-    foreach( const KUrl & prop, keys ) {
-        //kDebug() << "Prop " << prop;
-        
-        const QList<Soprano::Node> nodeList = simpleRes.values( prop );
-        res.setProperty( prop, Nepomuk::Variant::fromNodeList( nodeList ) );
-    }
-    return res.resourceUri();
-}
-
-Nepomuk::Resource Nepomuk::SyncFileIdentifier::additionalIdentification(const KUrl& uri)
-{
-    Sync::SimpleResource res = simpleResource( uri );
-
-    // Add the resource if ( it is NOT a FileDataObject ) or ( if is a FileDataObject and
-    // exists in the filesystem at the nie:url )
-    bool shouldAdd = !res.isFileDataObject();
-    if( res.isFileDataObject() ) {
-        if( QFile::exists( res.nieUrl().toLocalFile() ) )
-            shouldAdd = true;
-    }
-
-    if( shouldAdd ) {
-        Nepomuk::Resource newRes = createNewResource( res );
-        if( newRes.isValid() )
-            forceResource( uri, newRes );
-        else
-            return Resource();
-    }
-
-    return Resource();
-}
diff --git a/nepomuk/services/backupsync/service/syncfileidentifier.h b/nepomuk/services/backupsync/service/syncfileidentifier.h
deleted file mode 100644
index d913b04..0000000
--- a/nepomuk/services/backupsync/service/syncfileidentifier.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-    This file is part of the Nepomuk KDE project.
-    Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-#ifndef SYNCFILEIDENTIFIER_H
-#define SYNCFILEIDENTIFIER_H
-
-#include "resourceidentifier.h"
-#include "changelog.h"
-#include "identificationset.h"
-#include "syncfile.h"
-
-namespace Nepomuk {
-    
-    class SyncFileIdentifier : public Sync::ResourceIdentifier
-    {
-    public:
-        SyncFileIdentifier( const SyncFile & sf );
-        virtual ~SyncFileIdentifier();
-        
-        virtual void identifyAll();
-        int id();
-        
-        ChangeLog convertedChangeLog();
-        void load();
-
-    protected:
-        virtual Resource additionalIdentification(const KUrl& uri);
-        
-    private:
-        ChangeLog m_changeLog;
-        IdentificationSet m_identificationSet;
-        
-        static int NextId;
-        int m_id;
-
-        Resource createNewResource(const Nepomuk::Sync::SimpleResource& simpleRes) const;
-        
-    };
-}
-
-#endif // SYNCFILEIDENTIFIER_H
diff --git a/nepomuk/services/backupsync/service/syncmanager.cpp b/nepomuk/services/backupsync/service/syncmanager.cpp
index 95865ed..99eae53 100644
--- a/nepomuk/services/backupsync/service/syncmanager.cpp
+++ b/nepomuk/services/backupsync/service/syncmanager.cpp
@@ -20,7 +20,6 @@
 */
 
 #include "syncmanager.h"
-#include "identifier.h"
 #include "syncfile.h"
 #include "changelog.h"
 #include "diffgenerator.h"
@@ -35,9 +34,8 @@
 #include <Nepomuk/Resource>
 #include <Nepomuk/Variant>
 
-Nepomuk::SyncManager::SyncManager(Nepomuk::Identifier* ident, QObject* parent)
-    : QObject( parent ),
-      m_identifier( ident )
+Nepomuk::SyncManager::SyncManager(QObject* parent)
+    : QObject( parent )
 {
     new SyncManagerAdaptor( this );
     // Register DBus Object
@@ -50,20 +48,6 @@ Nepomuk::SyncManager::~SyncManager()
 {
 }
 
-
-int Nepomuk::SyncManager::sync(const QString& url)
-{
-    return sync( QUrl( url ) );
-}
-
-
-int Nepomuk::SyncManager::sync(const QUrl& url)
-{
-    SyncFile syncFile( url );
-    return m_identifier->process( syncFile );
-}
-
-
 void Nepomuk::SyncManager::createSyncFile( const QString& url, const QString& startTime )
 {
     ChangeLog logFile = LogStorage::instance()->getChangeLog( startTime );
@@ -116,6 +100,7 @@ void Nepomuk::SyncManager::createSyncFile(const QUrl& outputUrl, QSet<QUrl> & ne
 
 void Nepomuk::SyncManager::createFirstSyncFile(const QUrl& outputUrl) const
 {
+    Q_UNUSED(outputUrl);
     //SyncFile sf = Nepomuk::createFirstSyncFile();
     //sf.save( outputUrl );
 }
diff --git a/nepomuk/services/backupsync/service/syncmanager.h b/nepomuk/services/backupsync/service/syncmanager.h
index f6b2cbb..93fb7db 100644
--- a/nepomuk/services/backupsync/service/syncmanager.h
+++ b/nepomuk/services/backupsync/service/syncmanager.h
@@ -28,29 +28,24 @@
 
 namespace Nepomuk {
 
-    class Identifier;
     class SyncFile;
-    
+
     class SyncManager : public QObject
     {
         Q_OBJECT
         Q_CLASSINFO("D-Bus Interface", "org.kde.nepomuk.services.nepomukbackupsync.SyncManager")
-        
+
     public :
-        SyncManager(Identifier * ident, QObject * parent = 0);
+        SyncManager(QObject * parent = 0);
         virtual ~SyncManager();
 
     public slots:
-        int sync( const QString & url );
-        int sync( const QUrl & url );
-
         void createSyncFile( const QString & url, const QString& startTime );
         void createSyncFile( const QUrl& outputUrl, const QList<QString> & nieUrls );
         void createSyncFile( const QUrl& outputUrl, QSet<QUrl>& nepomukUris, const QDateTime & min );
         void createFirstSyncFile( const QUrl& outputUrl ) const;
 
     private :
-        Identifier * m_identifier;
     };
 }
 
diff --git a/nepomuk/services/backupsync/service/tools.cpp b/nepomuk/services/backupsync/service/tools.cpp
index 74517ec..317faf7 100644
--- a/nepomuk/services/backupsync/service/tools.cpp
+++ b/nepomuk/services/backupsync/service/tools.cpp
@@ -33,28 +33,37 @@
 
 #include <KTemporaryFile>
 #include <KDebug>
+#include "identificationset.h"
 
 
-void Nepomuk::saveBackupChangeLog(const QUrl& url)
+int Nepomuk::saveBackupChangeLog(const QUrl& url, QSet<QUrl> & uniqueUris )
 {
-    const int step = 100;
-    
-    ChangeLog changeLog;
-    
-    const QString query = QString::fromLatin1("select ?r ?p ?o ?g where { graph ?g { ?r ?p ?o. } ?g a nrl:InstanceBase . FILTER(!bif:exists( ( select (1) where { ?g a nrl:DiscardableInstanceBase . } ) )) . }");
-    
+    const int step = 1000;
+    const QString query = QString::fromLatin1("select ?r ?p ?o ?g where { "
+                                              "graph ?g { ?r ?p ?o. } "
+                                              "?g a nrl:InstanceBase . "
+                                              "FILTER(!bif:exists( ( select (1) where { ?g a nrl:DiscardableInstanceBase . } ) )) ."
+                                              "FILTER(regex(str(?r), '^nepomuk:/res/')). "
+                                              "}");
+
     Soprano::Model * model = Nepomuk::ResourceManager::instance()->mainModel();
-    
     Soprano::QueryResultIterator iter= model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
-    
-    int i=0;
+
+    int totalNumRecords = 0;
+    int i = 0;
+    ChangeLog changeLog;
     while( iter.next() ) {
         Soprano::Statement st( iter["r"], iter["p"], iter["o"], iter["g"] );
-         //kDebug() << st;
+
         changeLog += ChangeLogRecord( st );
+        totalNumRecords++;
+
+        uniqueUris.insert( st.subject().uri() );
+        if( st.object().isResource() && st.object().uri().scheme() == QLatin1String("nepomuk") )
+            uniqueUris.insert( st.object().uri() );
 
         if( ++i >= step ) {
-            //kDebug() << "Saving .. " << changeLog.size();
+            kDebug() << "Saving .. " << changeLog.size();
             changeLog.save( url );
             changeLog.clear();
             i = 0;
@@ -62,27 +71,23 @@ void Nepomuk::saveBackupChangeLog(const QUrl& url)
     }
 
     changeLog.save( url );
+    kDebug() << "Total Records : " << totalNumRecords;
+    return totalNumRecords;
 }
 
-//TODO: This doesn't really solve the problem that the backup maybe huge and
-//      large parts of the memory may get used. The proper solution would be
-//      to re-implement ChangeLog and IdentSet so that they don't load everything
-//      in one go.
-
 bool Nepomuk::saveBackupSyncFile(const QUrl& url)
 {
-    KTemporaryFile file;
-    file.open();
+    kDebug() << url;
+    KTemporaryFile logFile;
+    logFile.open();
 
-    saveBackupChangeLog( file.fileName() );
-    ChangeLog log = ChangeLog::fromUrl( file.fileName() );
-    kDebug() << "Log size: " << log.size();
+    QSet<QUrl> uniqueUris;
+    saveBackupChangeLog( logFile.fileName(), uniqueUris );
 
-    if( log.empty() ) {
-        kDebug() << "Nothing to save..";
-        return false;
-    }
-    
-    SyncFile syncFile( log );
-    return syncFile.save( url );
+    KTemporaryFile identificationFile;
+    identificationFile.open();
+    const QUrl identUrl( identificationFile.fileName() );
+
+    IdentificationSet::createIdentificationSet( uniqueUris, identUrl );
+    return SyncFile::createSyncFile( logFile.fileName(), identUrl, url );
 }
diff --git a/nepomuk/services/backupsync/service/tools.h b/nepomuk/services/backupsync/service/tools.h
index 98164ef..c35e002 100644
--- a/nepomuk/services/backupsync/service/tools.h
+++ b/nepomuk/services/backupsync/service/tools.h
@@ -36,8 +36,12 @@ namespace Nepomuk {
     /**
      * Saves a changeLog with the list of all the statements that should be backed up.
      * It's useful in when doing a first sync or first backup.
+     *
+     * \param uniqueUris After execution, it will contain a list of unique uris
+     * 
+     * Returns the numbers of records in the changelog
      */
-    void saveBackupChangeLog( const QUrl& url );
+    int saveBackupChangeLog( const QUrl& url, QSet<QUrl> & uniqueUris );
 
     bool saveBackupSyncFile( const QUrl& url );
 }
diff --git a/nepomuk/services/filewatch/CMakeLists.txt b/nepomuk/services/filewatch/CMakeLists.txt
index 753f621..d480b3c 100644
--- a/nepomuk/services/filewatch/CMakeLists.txt
+++ b/nepomuk/services/filewatch/CMakeLists.txt
@@ -10,6 +10,7 @@ include_directories(
   ${SOPRANO_INCLUDE_DIR}
   ${NEPOMUK_INCLUDE_DIR}
   ${CMAKE_CURRENT_SOURCE_DIR}/../strigi
+  ${CMAKE_CURRENT_SOURCE_DIR}/../storage/lib/
   )
 
 set(SRCS
@@ -18,10 +19,10 @@ set(SRCS
   updaterequest.cpp
   invalidfileresourcecleaner.cpp
   ../strigi/strigiserviceconfig.cpp
+  removabledeviceindexnotification.cpp
   )
 
 qt4_add_dbus_interface(SRCS ../../interfaces/org.kde.nepomuk.Strigi.xml strigiserviceinterface)
-qt4_add_dbus_interface(SRCS ../../interfaces/org.kde.nepomuk.RemovableStorage.xml removablestorageserviceinterface)
 
 if(CMAKE_SYSTEM_NAME MATCHES "Linux")
   set(SRCS
@@ -35,10 +36,12 @@ kde4_add_plugin(nepomukfilewatch ${SRCS})
 
 target_link_libraries(nepomukfilewatch
   nepomukcommon
+  nepomukdatamanagement
   ${SOPRANO_CLIENT_LIBRARIES}
   ${SOPRANO_LIBRARIES}
   ${KDE4_KDEUI_LIBS}
   ${KDE4_KIO_LIBS}
+  ${KDE4_SOLID_LIBS}
   ${NEPOMUK_LIBRARIES}
   ${NEPOMUK_QUERY_LIBRARIES}
   )
@@ -49,5 +52,8 @@ install(
 install(
   TARGETS nepomukfilewatch
   DESTINATION ${PLUGIN_INSTALL_DIR})
+install(
+  FILES nepomukfilewatch.notifyrc
+  DESTINATION ${DATA_INSTALL_DIR}/nepomukfilewatch)
 
 add_subdirectory(test)
diff --git a/nepomuk/services/filewatch/invalidfileresourcecleaner.cpp b/nepomuk/services/filewatch/invalidfileresourcecleaner.cpp
index f1e0478..a4bb346 100644
--- a/nepomuk/services/filewatch/invalidfileresourcecleaner.cpp
+++ b/nepomuk/services/filewatch/invalidfileresourcecleaner.cpp
@@ -23,6 +23,7 @@
 
 #include <QtCore/QList>
 #include <QtCore/QFile>
+#include <QtCore/QTimer>
 
 #include <Soprano/Node>
 #include <Soprano/Model>
@@ -52,40 +53,31 @@ Nepomuk::InvalidFileResourceCleaner::~InvalidFileResourceCleaner()
     wait();
 }
 
-namespace {
-
-    /* BUG: This is a workaround to "Removable Storage" or "Network Storage" bug, where the metadata of all
-     * the files in the NAS would be deleted if the NAS was unmounted and Nepomuk was restarted.
-     *
-     * FIXME: This is NOT a permanent fix and should be removed in 4.7
-     */ 
-    QString deletionBlacklistFilter() {
-        KConfig config("nepomukserverrc");
-        QStringList directories = config.group("Service-nepomukfilewatch").readPathEntry("Deletion Blacklist", QStringList());
-        
-        QString filter;
-        Q_FOREACH( const QString & dir, directories ) {
-            filter += QString::fromLatin1( " && !regex(str(?url), '^%1') " )
-                      .arg( KUrl(dir).url( KUrl::AddTrailingSlash ) );
-        }
-        return filter;
-    }
-}
 
 void Nepomuk::InvalidFileResourceCleaner::run()
 {
     kDebug() << "Searching for invalid local file entries";
+#ifndef NDEBUG
+    QTime timer;
+    timer.start();
+#endif
     //
     // Since the removal of the graphs could intefere with the iterator and result
-    // in jumping of rows (worst case) we cache all graphs to remove
+    // in jumping of rows (worst case) we cache all resources to remove
     //
     QList<Soprano::Node> resourcesToRemove;
-    QString query = QString::fromLatin1( "select distinct ?r ?url where { "
-                                         "?r %1 ?url. "
-                                         "FILTER(regex(str(?url), 'file://') %2 ). }" )
-                    .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                          deletionBlacklistFilter() );
-    Soprano::QueryResultIterator it = Nepomuk::ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
+    QString basePathFilter;
+    if(!m_basePath.isEmpty()) {
+        basePathFilter = QString::fromLatin1("FILTER(REGEX(STR(?url), '^%1')) . ")
+                .arg(KUrl(m_basePath).url(KUrl::AddTrailingSlash));
+    }
+    const QString query
+            = QString::fromLatin1( "select distinct ?r ?url where { "
+                                   "?r %1 ?url . %2}" )
+            .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
+                  basePathFilter );
+    Soprano::QueryResultIterator it
+            = Nepomuk::ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
 
     while( it.next() && !m_stopped ) {
         QUrl url( it["url"].uri() );
@@ -100,10 +92,20 @@ void Nepomuk::InvalidFileResourceCleaner::run()
     Q_FOREACH( const Soprano::Node& r, resourcesToRemove ) {
         if ( m_stopped )
             break;
+        // TODO: use DMS here instead of Soprano
         Nepomuk::ResourceManager::instance()->mainModel()->removeAllStatements( r, Soprano::Node(), Soprano::Node() );
         Nepomuk::ResourceManager::instance()->mainModel()->removeAllStatements( Soprano::Node(), Soprano::Node(), r );
     }
     kDebug() << "Done searching for invalid local file entries";
+#ifndef NDEBUG
+    kDebug() << "Time elapsed: " << timer.elapsed()/1000.0 << "sec";
+#endif
+}
+
+void Nepomuk::InvalidFileResourceCleaner::start(const QString &basePath)
+{
+    m_basePath = basePath;
+    start();
 }
 
 #include "invalidfileresourcecleaner.moc"
diff --git a/nepomuk/services/filewatch/invalidfileresourcecleaner.h b/nepomuk/services/filewatch/invalidfileresourcecleaner.h
index 61e76e8..b3501bd 100644
--- a/nepomuk/services/filewatch/invalidfileresourcecleaner.h
+++ b/nepomuk/services/filewatch/invalidfileresourcecleaner.h
@@ -53,10 +53,19 @@ namespace Nepomuk {
          */
         ~InvalidFileResourceCleaner();
 
+        /**
+         * Start the cleaner thread with a base path, ie. only
+         * check files under that optional path.
+         */
+        void start(const QString& basePath);
+
+        using QThread::start;
+
     private:
         void run();
 
         bool m_stopped;
+        QString m_basePath;
     };
 }
 
diff --git a/nepomuk/services/filewatch/metadatamover.cpp b/nepomuk/services/filewatch/metadatamover.cpp
index b1ca616..8db2ecf 100644
--- a/nepomuk/services/filewatch/metadatamover.cpp
+++ b/nepomuk/services/filewatch/metadatamover.cpp
@@ -18,31 +18,16 @@
 
 #include "metadatamover.h"
 #include "nepomukfilewatch.h"
+#include "datamanagement.h"
 
-#include <QtCore/QDir>
-#include <QtCore/QRegExp>
-#include <QtCore/QFileInfo>
 #include <QtCore/QTimer>
 
 #include <Soprano/Model>
-#include <Soprano/StatementIterator>
-#include <Soprano/Statement>
 #include <Soprano/Node>
 #include <Soprano/NodeIterator>
 #include <Soprano/QueryResultIterator>
 #include <Soprano/LiteralValue>
-#include <Soprano/Vocabulary/Xesam>
-
-#include <Nepomuk/Resource>
-#include <Nepomuk/ResourceManager>
-#include <Nepomuk/Variant>
-#include <Nepomuk/Types/Property>
-#include <Nepomuk/Query/Query>
-#include <Nepomuk/Query/ComparisonTerm>
-#include <Nepomuk/Query/ResourceTerm>
-#include <Nepomuk/Query/LiteralTerm>
-
-#include <Nepomuk/Vocabulary/NFO>
+
 #include <Nepomuk/Vocabulary/NIE>
 
 #include <KDebug>
@@ -51,8 +36,7 @@
 Nepomuk::MetadataMover::MetadataMover( Soprano::Model* model, QObject* parent )
     : QThread( parent ),
       m_stopped(false),
-      m_model( model ),
-      m_strigiParentUrlUri( "http://strigi.sf.net/ontologies/0.9#parentUrl" )
+      m_model( model )
 {
     QTimer* timer = new QTimer( this );
     connect( timer, SIGNAL( timeout() ), this, SLOT( slotClearRecentlyFinishedRequests() ) );
@@ -77,6 +61,8 @@ void Nepomuk::MetadataMover::stop()
 void Nepomuk::MetadataMover::moveFileMetadata( const KUrl& from, const KUrl& to )
 {
     kDebug() << from << to;
+    Q_ASSERT( !from.path().isEmpty() && from.path() != "/" );
+    Q_ASSERT( !to.path().isEmpty() && to.path() != "/" );
     m_queueMutex.lock();
     UpdateRequest req( from, to );
     if ( !m_updateQueue.contains( req ) &&
@@ -89,6 +75,7 @@ void Nepomuk::MetadataMover::moveFileMetadata( const KUrl& from, const KUrl& to
 
 void Nepomuk::MetadataMover::removeFileMetadata( const KUrl& file )
 {
+    Q_ASSERT( !file.path().isEmpty() && file.path() != "/" );
     removeFileMetadata( KUrl::List() << file );
 }
 
@@ -127,16 +114,6 @@ void Nepomuk::MetadataMover::run()
 
             kDebug() << "========================= handling" << updateRequest.source() << updateRequest.target();
 
-            //
-            // IMPORTANT: clear the ResourceManager cache since otherwise the following will happen
-            // (see also ResourceData::invalidateCache):
-            // We update the nie:url for resource X. Afterwards we get a fileDeleted event for the
-            // old URL. That one is still in the ResourceManager cache and will bring us directly
-            // to resource X. As a result we will delete all its metadata.
-            // This is a bug in ResourceManager for which I (trueg) have not found a good solution yet.
-            //
-            ResourceManager::instance()->clearCache();
-
             // an empty second url means deletion
             if( updateRequest.target().isEmpty() ) {
                 KUrl url = updateRequest.source();
@@ -177,12 +154,9 @@ void Nepomuk::MetadataMover::removeMetadata( const KUrl& url )
         kDebug() << "empty path. Looks like a bug somewhere...";
     }
     else {
-        bool isFolder = url.url().endsWith('/');
-        Resource res( url );
-        if ( res.exists() ) {
-            kDebug() << "removing metadata for file" << url << "with resource URI" << res.resourceUri();
-            res.remove();
-        }
+        const bool isFolder = url.url().endsWith('/');
+        kDebug() << "removing metadata for file" << url;
+        Nepomuk::removeResources(QList<QUrl>() << url);
 
         if( isFolder ) {
             //
@@ -207,15 +181,17 @@ void Nepomuk::MetadataMover::removeMetadata( const KUrl& url )
             // cached items.
             //
             while ( 1 ) {
-                QList<Soprano::Node> urls = m_model->executeQuery( query + QLatin1String( " LIMIT 100" ),
-                                                                Soprano::Query::QueryLanguageSparql )
-                                            .iterateBindings( 0 ).allNodes();
-                if ( urls.isEmpty() )
+                QList<QUrl> urls;
+                Soprano::QueryResultIterator it = m_model->executeQuery( query + QLatin1String( " LIMIT 20" ),
+                                                                         Soprano::Query::QueryLanguageSparql );
+                while(it.next()) {
+                    urls << it[0].uri();
+                }
+                if ( !urls.isEmpty() ) {
+                    Nepomuk::removeResources(urls);
+                }
+                else {
                     break;
-
-                for ( int i = 0; i < urls.count(); ++i ) {
-                    // the old URL of the resource to update
-                    Resource::fromResourceUri( urls[i].uri() ).remove();
                 }
             }
         }
@@ -223,93 +199,14 @@ void Nepomuk::MetadataMover::removeMetadata( const KUrl& url )
 }
 
 
-void Nepomuk::MetadataMover::updateMetadata( const KUrl& from, const KUrl& to, bool includeChildren )
+void Nepomuk::MetadataMover::updateMetadata( const KUrl& from, const KUrl& to )
 {
     kDebug() << from << "->" << to;
 
-    //
-    // Since KDE 4.4 we use nepomuk:/res/<UUID> Uris for all resources including files. Thus, moving a file
-    // means updating two things:
-    // 1. the nie:url property
-    // 2. the nie:isPartOf relation (only necessary if the file and not the whole folder was moved)
-    //
-    // However, since we will still have file:/ resource URIs from old data we will also handle that
-    // and convert them to new nepomuk:/res/<UUID> style URIs.
-    //
-
-    Resource oldResource( from );
-
-    if ( oldResource.exists() ) {
-        const QUrl oldResourceUri = oldResource.resourceUri();
-        QUrl newResourceUri( oldResourceUri );
-
-        //
-        // Handle legacy file:/ resource URIs
-        //
-        if ( from.equals( oldResourceUri, KUrl::CompareWithoutTrailingSlash ) ) {
-            newResourceUri = updateLegacyMetadata( oldResourceUri );
-        }
-
-        //
-        // Now update the nie:url, nfo:fileName, and nie:isPartOf relations.
-        //
-        // We do NOT use Resource::setProperty to avoid the overhead and data clutter of creating
-        // new metadata graphs for the changed data.
-        //
-        // TODO: put this into a nice API which can act as the low-level version of Nepomuk::Resource
-        //
-        QString query = QString::fromLatin1( "select distinct ?g ?u ?f ?p where { "
-                                             "graph ?g { %1 ?prop ?obj . "
-                                             "OPTIONAL { %1 %2 ?u . } . "
-                                             "OPTIONAL { %1 %3 ?f . } . "
-                                             "OPTIONAL { %1 %4 ?p . } . "
-                                             "} . "
-                                             "FILTER(BOUND(?u) || BOUND(?f) || BOUND(?p)) . }" )
-                        .arg( Soprano::Node::resourceToN3( newResourceUri ),
-                              Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                              Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NFO::fileName() ),
-                              Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::isPartOf() ) );
-        QList<Soprano::BindingSet> graphs
-            = m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql ).allBindings();
-
-        Q_FOREACH( const Soprano::BindingSet& graph, graphs ) {
-
-            if ( graph["u"].isValid() ) {
-                m_model->removeStatement( newResourceUri,
-                                          Nepomuk::Vocabulary::NIE::url(),
-                                          graph["u"],
-                                          graph["g"] );
-                m_model->addStatement( newResourceUri,
-                                       Nepomuk::Vocabulary::NIE::url(),
-                                       to,
-                                       graph["g"] );
-            }
-
-            if ( graph["f"].isValid() ) {
-                m_model->removeStatement( newResourceUri,
-                                          Nepomuk::Vocabulary::NFO::fileName(),
-                                          graph["f"],
-                                          graph["g"] );
-                m_model->addStatement( newResourceUri,
-                                       Nepomuk::Vocabulary::NFO::fileName(),
-                                       Soprano::LiteralValue( to.fileName() ),
-                                       graph["g"] );
-            }
-
-            if ( graph["p"].isValid() ) {
-                m_model->removeStatement( newResourceUri,
-                                          Nepomuk::Vocabulary::NIE::isPartOf(),
-                                          graph["p"],
-                                          graph["g"] );
-                Resource newParent( to.directory( KUrl::IgnoreTrailingSlash ) );
-                if ( newParent.exists() ) {
-                    m_model->addStatement( newResourceUri,
-                                           Nepomuk::Vocabulary::NIE::isPartOf(),
-                                           newParent.resourceUri(),
-                                           graph["g"] );
-                }
-            }
-        }
+    if ( m_model->executeQuery(QString::fromLatin1("ask where { { %1 ?p ?o . } UNION { ?r nie:url %1 . } . }")
+                               .arg(Soprano::Node::resourceToN3(from)),
+                               Soprano::Query::QueryLanguageSparql).boolValue() ) {
+        Nepomuk::setProperty(QList<QUrl>() << from, Nepomuk::Vocabulary::NIE::url(), QVariantList() << to);
     }
     else {
         //
@@ -318,107 +215,6 @@ void Nepomuk::MetadataMover::updateMetadata( const KUrl& from, const KUrl& to, b
         //
         emit movedWithoutData( to.directory( KUrl::IgnoreTrailingSlash ) );
     }
-
-    if ( includeChildren && QFileInfo( to.toLocalFile() ).isDir() ) {
-        //
-        // Recursively update children
-        // We cannot use the nie:isPartOf relation since only children could have metadata. Thus, we do a regex
-        // match on all files and folders below the URL we got.
-        //
-        // CAUTION: The trailing slash on the from URL is essential! Otherwise we might match the newly added
-        //          URLs, too (in case a rename only added chars to the name)
-        //
-        const QString query = QString::fromLatin1( "select distinct ?url where { "
-                                                   "?r %1 ?url . "
-                                                   "FILTER(REGEX(STR(?url),'^%2')) . "
-                                                   "}" )
-                              .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                                    from.url(KUrl::AddTrailingSlash) );
-        kDebug() << query;
-
-        const QString oldBasePath = from.path( KUrl::AddTrailingSlash );
-        const QString newBasePath = to.path( KUrl::AddTrailingSlash );
-
-        //
-        // We cannot use one big loop since our updateMetadata calls below can change the iterator
-        // which could have bad effects like row skipping. Thus, we handle the urls in chunks of
-        // cached items.
-        //
-        while ( 1 ) {
-            QList<Soprano::Node> urls = m_model->executeQuery( query + QLatin1String( " LIMIT 100" ),
-                                                               Soprano::Query::QueryLanguageSparql )
-                                        .iterateBindings( 0 ).allNodes();
-            if ( urls.isEmpty() )
-                break;
-
-            for ( int i = 0; i < urls.count(); ++i ) {
-                // the old URL of the resource to update
-                const KUrl url = urls[i].uri();
-
-                // now construct the new URL
-                QString oldRelativePath = url.path().mid( oldBasePath.length() );
-                KUrl newUrl( newBasePath + oldRelativePath );
-
-                // finally update the metadata (excluding children since we already handle them all here)
-                updateMetadata( url, newUrl, false );
-            }
-        }
-    }
-}
-
-
-QUrl Nepomuk::MetadataMover::updateLegacyMetadata( const QUrl& oldResourceUri )
-{
-    kDebug() << oldResourceUri;
-
-    //
-    // Get a new resource URI
-    //
-    QUrl newResourceUri = ResourceManager::instance()->generateUniqueUri( QString() );
-
-    //
-    // update the resource properties
-    //
-    QList<Soprano::Statement> sl = m_model->listStatements( oldResourceUri,
-                                                            Soprano::Node(),
-                                                            Soprano::Node() ).allStatements();
-    Q_FOREACH( const Soprano::Statement& s, sl ) {
-        //
-        // Skip all the stuff we will update later on
-        //
-        if ( s.predicate() == Soprano::Vocabulary::Xesam::url() ||
-             s.predicate() == Nepomuk::Vocabulary::NIE::url() ||
-             s.predicate() == m_strigiParentUrlUri ||
-             s.predicate() == Nepomuk::Vocabulary::NIE::isPartOf() ) {
-            continue;
-        }
-
-        m_model->addStatement( newResourceUri,
-                               s.predicate(),
-                               s.object(),
-                               s.context() );
-    }
-
-    m_model->removeStatements( sl );
-
-
-    //
-    // update resources relating to it
-    //
-    sl = m_model->listStatements( Soprano::Node(),
-                                  Soprano::Node(),
-                                  oldResourceUri ).allStatements();
-    Q_FOREACH( const Soprano::Statement& s, sl ) {
-        m_model->addStatement( s.subject(),
-                               s.predicate(),
-                               newResourceUri,
-                               s.context() );
-    }
-    m_model->removeStatements( sl );
-
-    kDebug() << "->" << newResourceUri;
-
-    return newResourceUri;
 }
 
 
diff --git a/nepomuk/services/filewatch/metadatamover.h b/nepomuk/services/filewatch/metadatamover.h
index c5f5925..30a8ef7 100644
--- a/nepomuk/services/filewatch/metadatamover.h
+++ b/nepomuk/services/filewatch/metadatamover.h
@@ -74,20 +74,8 @@ namespace Nepomuk {
         /**
          * Recursively update the nie:url and nie:isPartOf properties
          * of the resource describing \p from.
-         *
-         * If old pre-KDE 4.4 file:/ resource URIs are used these are
-         * updated to the new nepomuk:/res/<UUID> scheme
          */
-        void updateMetadata( const KUrl& from, const KUrl& to, bool includeChildren = true );
-
-        /**
-         * Convert old pre-KDE 4.4 style file:/ resource URIs to the
-         * new nepomuk:/res/<UUID> scheme.
-         *
-         * \return The new resource URI. This resource does not have
-         * nie:url or nie:isPartOf properties yet.
-         */
-        QUrl updateLegacyMetadata( const QUrl& oldResourceUri );
+        void updateMetadata( const KUrl& from, const KUrl& to );
 
         // if the second url is empty, just delete the metadata
         QQueue<UpdateRequest> m_updateQueue;
@@ -105,8 +93,6 @@ namespace Nepomuk {
         bool m_stopped;
 
         Soprano::Model* m_model;
-
-        const QUrl m_strigiParentUrlUri;
     };
 }
 
diff --git a/nepomuk/services/filewatch/nepomukfilewatch.cpp b/nepomuk/services/filewatch/nepomukfilewatch.cpp
index 2a49df5..fad7d0d 100644
--- a/nepomuk/services/filewatch/nepomukfilewatch.cpp
+++ b/nepomuk/services/filewatch/nepomukfilewatch.cpp
@@ -21,6 +21,8 @@
 #include "strigiserviceinterface.h"
 #include "fileexcludefilters.h"
 #include "invalidfileresourcecleaner.h"
+#include "removabledeviceindexnotification.h"
+#include "removablemediacache.h"
 #include "../strigi/strigiserviceconfig.h"
 
 #ifdef BUILD_KINOTIFY
@@ -34,6 +36,7 @@
 #include <KDebug>
 #include <KUrl>
 #include <KPluginFactory>
+#include <KConfigGroup>
 
 #include <Nepomuk/ResourceManager>
 #include <Nepomuk/Vocabulary/NIE>
@@ -155,6 +158,12 @@ Nepomuk::FileWatch::FileWatch( QObject* parent, const QList<QVariant>& )
     connectToKDirWatch();
 #endif
 
+    // we automatically watch newly mounted media - it is very unlikely that anything non-interesting is mounted
+    m_removableMediaCache = new RemovableMediaCache(this);
+    connect(m_removableMediaCache, SIGNAL(deviceMounted(const Nepomuk::RemovableMediaCache::Entry*)),
+            this, SLOT(slotDeviceMounted(const Nepomuk::RemovableMediaCache::Entry*)));
+    addWatchesForMountedRemovableMedia();
+
     (new InvalidFileResourceCleaner(this))->start();
     
     connect( StrigiServiceConfig::self(), SIGNAL( configChanged() ),
@@ -225,13 +234,13 @@ void Nepomuk::FileWatch::slotFileDeleted( const QString& urlString, bool isDir )
 void Nepomuk::FileWatch::slotFileCreated( const QString& path )
 {
     kDebug() << path;
-    updateFolderViaStrigi( path );
+    updateFileViaStrigi( path );
 }
 
 
 void Nepomuk::FileWatch::slotFileModified( const QString& path )
 {
-    updateFolderViaStrigi( path );
+    updateFileViaStrigi( path );
 }
 
 
@@ -242,6 +251,18 @@ void Nepomuk::FileWatch::slotMovedWithoutData( const QString& path )
 
 
 // static
+void Nepomuk::FileWatch::updateFileViaStrigi(const QString &path)
+{
+    if( StrigiServiceConfig::self()->shouldBeIndexed(path) ) {
+        org::kde::nepomuk::Strigi strigi( "org.kde.nepomuk.services.nepomukstrigiservice", "/nepomukstrigiservice", QDBusConnection::sessionBus() );
+        if ( strigi.isValid() ) {
+            strigi.indexFile( path );
+        }
+    }
+}
+
+
+// static
 void Nepomuk::FileWatch::updateFolderViaStrigi( const QString& path )
 {
     if( StrigiServiceConfig::self()->shouldBeIndexed(path) ) {
@@ -299,4 +320,62 @@ void Nepomuk::FileWatch::updateIndexedFoldersWatches()
 #endif
 }
 
+
+void Nepomuk::FileWatch::addWatchesForMountedRemovableMedia()
+{
+    Q_FOREACH(const RemovableMediaCache::Entry* entry, m_removableMediaCache->allMedia()) {
+        if(entry->isMounted())
+            slotDeviceMounted(entry);
+    }
+}
+
+void Nepomuk::FileWatch::slotDeviceMounted(const Nepomuk::RemovableMediaCache::Entry* entry)
+{
+    //
+    // now that the device is mounted we can clean up our db - in case we have any
+    // data for file that have been deleted from the device in the meantime.
+    //
+    InvalidFileResourceCleaner* cleaner = new InvalidFileResourceCleaner(this);
+    cleaner->start(entry->mountPath());
+
+    //
+    // tell Strigi to update the newly mounted device
+    //
+    KConfig strigiConfig( "nepomukstrigirc" );
+    int index = 0;
+    if(strigiConfig.group("Devices").hasKey(entry->url())) {
+        index = strigiConfig.group("Devices").readEntry(entry->url(), false) ? 1 : -1;
+    }
+
+    const bool indexNewlyMounted = strigiConfig.group( "RemovableMedia" ).readEntry( "index newly mounted", false );
+    const bool askIndividually = strigiConfig.group( "RemovableMedia" ).readEntry( "ask user", false );
+
+    if( index == 0 && indexNewlyMounted && !askIndividually ) {
+        index = 1;
+    }
+
+    // index automatically
+    if( index == 1 ) {
+        kDebug() << "Device configured for automatic indexing. Calling Strigi service.";
+        org::kde::nepomuk::Strigi strigi( "org.kde.nepomuk.services.nepomukstrigiservice", "/nepomukstrigiservice", QDBusConnection::sessionBus() );
+        if ( strigi.isValid() ) {
+            strigi.indexFolder( entry->mountPath(), true /* recursive */, false /* no forced update */ );
+        }
+    }
+
+    // ask the user if we should index
+    else if( index == 0 && indexNewlyMounted && askIndividually ) {
+        kDebug() << "Device unknown. Asking user for action.";
+        (new RemovableDeviceIndexNotification(entry, this))->sendEvent();
+    }
+
+    else {
+        // TODO: remove all the indexed info
+        kDebug() << "Device configured to not be indexed.";
+    }
+
+    kDebug() << "Installing watch for removable storage at mount point" << entry->mountPath();
+    watchFolder(entry->mountPath());
+}
+
 #include "nepomukfilewatch.moc"
diff --git a/nepomuk/services/filewatch/nepomukfilewatch.desktop b/nepomuk/services/filewatch/nepomukfilewatch.desktop
index 8cc7e95..e79c0aa 100644
--- a/nepomuk/services/filewatch/nepomukfilewatch.desktop
+++ b/nepomuk/services/filewatch/nepomukfilewatch.desktop
@@ -11,6 +11,7 @@ Name[be@latin]=NepomukFileWatch
 Name[bg]=NepomukFileWatch
 Name[bn]=NepomukFileWatch
 Name[bn_IN]=NepomukFileWatch
+Name[bs]=Nepomukov nadzor datoteka
 Name[ca]=NepomukFileWatch
 Name[ca@valencia]=NepomukFileWatch
 Name[cs]=Nepomuk - sledování souboru 
@@ -75,6 +76,7 @@ Name[te]=Nepomukఫైల్ వాచ్
 Name[tg]=NepomukFileWatch
 Name[th]=NepomukFileWatch
 Name[tr]=NepomukFileWatch
+Name[ug]=Nepomuk ھۆججەت نازارەت
 Name[uk]=NepomukFileWatch
 Name[wa]=RiwaitaedjeFitchîNepomuk
 Name[x-test]=xxNepomukFileWatchxx
@@ -85,6 +87,7 @@ Comment[ar]=تقوم خدمة نبومك لمراقبة الملفات برصد
 Comment[ast]=Serviciu de monitor de ficheros de Nepomuk pa monitorizar cambeos de los ficheros
 Comment[be@latin]=Słužba „Nepomuk”, jakaja naziraje za źmienami ŭ fajłach
 Comment[bg]=Услугата NepomukFileWatch следи за промени във файловете
+Comment[bs]=Nepomukov servis za nadgledanje izmjena nad datotekema
 Comment[ca]=El servei de control de fitxers del Nepomuk per fer un seguiment dels canvis en fitxers
 Comment[ca@valencia]=El servei de control de fitxers del Nepomuk per fer un seguiment dels canvis en fitxers
 Comment[cs]=Služba Nepomuku, která sleduje změny v souborech
@@ -96,7 +99,7 @@ Comment[en_GB]=The Nepomuk file watch service for monitoring file changes
 Comment[eo]=La atend-servo de Nepomuk dosiero por monitora dosiero ŝanĝiĝis.
 Comment[es]=Servicio de monitor de archivos de Nepomuk para monitorizar cambios de los archivos
 Comment[et]=Nepomuki failijälgimise teenus failide muutuste jälgimiseks
-Comment[eu]=Nepomuk fitxategi zaiintzailea, fitxategien aldaketak monitorizatzeko
+Comment[eu]=Nepomuken fitxategiak zaintzeko zerbitzua, fitxategien aldaketak zelatatzeko
 Comment[fi]=Tiedostojen muutoksia tarkkaileva Nepomuk-tiedostonseurantapalvelu
 Comment[fr]=Le service de surveillance des fichiers, pour relever les modifications de fichiers
 Comment[fy]=De Nepomuk triem observaasje tsjinst foar triemferoarings
@@ -121,7 +124,7 @@ Comment[ko]=파일 변화를 감시하는 Nepumuk 파일 감시 서비스
 Comment[ku]=Nepomuk Servîsa nihêrîna pelê ji bo dîtina guherînan
 Comment[lt]=Nepomuk failų stebėjimo tarnyba failų pakeitimams sekti
 Comment[lv]=Nepomuk failu novērošanas serviss, kas novēro izmaiņas failos
-Comment[mai]=फाइल परिवर्तनक मानिटरिंगक लेल नेपोमक फाइल निगरानी सेवा
+Comment[mai]=फ़ाइल परिवर्तनक मानिटरिंगक लेल नेपोमक फ़ाइल निगरानी सेवा
 Comment[ml]=ഫയല്‍ മാറ്റങ്ങള്‍ നിരീക്ഷിക്കുവാനുള്ള നെപ്പോമുക്ക് ഫയല്‍ നിരീക്ഷണ സേവനം.
 Comment[nb]=Nepomuk filovervåkning for å detektere filendringer
 Comment[nds]=Nepomuk sien Dateibeluurdeenst, de Ännern an Dateien vermeldt
@@ -147,6 +150,7 @@ Comment[te]=ఫైల్ మార్పులను మానిటరింగ
 Comment[tg]=Системаи Nepomuk тағйиротҳоро дар файлҳо муайян мекунад
 Comment[th]=บริการของ Nepomuk สำหรับคอยตรวจจับความเปลี่ยนแปลงของแฟ้ม
 Comment[tr]=Dosya değişikliklerini izlemek için Nepomuk dosya izleme servisi
+Comment[ug]=ھۆججەت ئۆزگىرىشىنى نازارەت قىلىدىغان Nepomuk ھۆججەت نازارەت مۇلازىمىتى
 Comment[uk]=Служба Nepomuk для спостереження за змінами в файлах
 Comment[wa]=Li siervice di rwaitaedje des fitchî di Nepomuk po corwaitî les candjmints e fitchîs
 Comment[x-test]=xxThe Nepomuk file watch service for monitoring file changesxx
diff --git a/nepomuk/services/filewatch/nepomukfilewatch.h b/nepomuk/services/filewatch/nepomukfilewatch.h
index cac403e..3e61b91 100644
--- a/nepomuk/services/filewatch/nepomukfilewatch.h
+++ b/nepomuk/services/filewatch/nepomukfilewatch.h
@@ -25,6 +25,8 @@
 #include <QtCore/QVariant>
 #include <QtCore/QSet>
 
+#include "removablemediacache.h"
+
 namespace Soprano {
     class Model;
     namespace Client {
@@ -50,6 +52,12 @@ namespace Nepomuk {
         ~FileWatch();
 
         /**
+         * Tells strigi to update the file (it can also be a folder but
+         * then updating will not be recursive) at \p path.
+         */
+        static void updateFileViaStrigi( const QString& path );
+
+        /**
          * Tells strigi to update the folder at \p path or the folder
          * containing \p path in case it is a file.
          */
@@ -76,8 +84,20 @@ namespace Nepomuk {
          */
         void updateIndexedFoldersWatches();
 
+        /**
+         * Connected to each removable media. Adds a watch for the mount point,
+         * cleans up the index with respect to removed files, and optionally
+         * tells the indexer service to run on the mount path.
+         */
+        void slotDeviceMounted( const Nepomuk::RemovableMediaCache::Entry* );
+
     private:
         /**
+         * Adds watches for all mounted removable media.
+         */
+        void addWatchesForMountedRemovableMedia();
+
+        /**
          * Returns true if the path is one that should be always ignored.
          * This includes such things like temporary files and folders as
          * they are created for example by build systems.
@@ -91,6 +111,7 @@ namespace Nepomuk {
 #endif
 
         RegExpCache* m_pathExcludeRegExpCache;
+        RemovableMediaCache* m_removableMediaCache;
     };
 }
 
diff --git a/nepomuk/services/filewatch/nepomukfilewatch.notifyrc b/nepomuk/services/filewatch/nepomukfilewatch.notifyrc
new file mode 100644
index 0000000..b10e859
--- /dev/null
+++ b/nepomuk/services/filewatch/nepomukfilewatch.notifyrc
@@ -0,0 +1,148 @@
+[Global]
+IconName=nepomuk
+Comment=Nepomuk file watch service
+Comment[bg]=Услуга на Nepomuk за следене на файлове
+Comment[bs]=Servis Nepomuka
+Comment[ca]=Servei de control de fitxers del Nepomuk
+Comment[ca@valencia]=Servei de control de fitxers del Nepomuk
+Comment[cs]=Služba Nepomuku sledující soubory
+Comment[da]=Filovervågningstjeneste til Nepomuk
+Comment[de]=Nepomuk-Dienst zur Dateiüberwachung
+Comment[es]=Servicio de Nepomuk de observación de archivos
+Comment[et]=Nepomuki failijälgimise teenus
+Comment[eu]=Nepomuken fitxategiak zaintzeko zerbitzua
+Comment[fi]=Nepomuk-tiedostonseurantapalvelu
+Comment[fr]=Service de surveillance des fichiers de Nepomuk
+Comment[he]=משגוח הקבצים של Nepomuk
+Comment[hr]=Nepomukova usluga nadgledanja datoteka
+Comment[hu]=Nepomuk fájlfigyelő szolgáltatás
+Comment[ia]=Servicio de guarda de file de Nepomuk
+Comment[is]=Nepomuk skráavöktunarþjónusta
+Comment[it]=Servizio monitoraggio file di Nepomuk
+Comment[kk]=Nepomuk файл бақылау қызметі
+Comment[km]=សេវា​កម្មវិធី​មើល​ឯកសារ​របស់ Nepomuk
+Comment[kn]=ನೆಪೋಮುಕ್ ಕಡತ ಗಮನಿಸುವ ಸೇವೆ
+Comment[ko]=Nepomuk 파일 감시 서비스
+Comment[lt]=Nepomuk failų stebėjimo tarnyba
+Comment[lv]=Nepomuk failu novērošanas serviss
+Comment[nb]=Nepomuk filovervåkningstjeneste
+Comment[nds]=Nepomuk-Dateibeluurdeenst
+Comment[nl]=Nepomuk bestandenbewakingsdienst
+Comment[nn]=Filovervakingsteneste for Nepomuk
+Comment[pa]=ਨਿਪੋਮੁਕ ਫਾਇਲ ਵਾਚ ਸਰਵਿਸ
+Comment[pl]=Usługa Nepomuk obserwacji pliku
+Comment[pt]=Serviço de pesquisa de ficheiros do Nepomuk
+Comment[pt_BR]=O serviço de inspeção de arquivos do Nepomuk
+Comment[ro]=Serviciu de supraveghere a fișierelor Nepomuk
+Comment[ru]=Отслеживание изменений в файлах
+Comment[sl]=Nepomukova storitev za opazovanje datotek
+Comment[sr]=Непомуков сервис за надгледање фајлова
+Comment[sr@ijekavian]=Непомуков сервис за надгледање фајлова
+Comment[sr@ijekavianlatin]=Nepomukov servis za nadgledanje fajlova
+Comment[sr@latin]=Nepomukov servis za nadgledanje fajlova
+Comment[sv]=Nepomuk-tjänst för filövervakning
+Comment[th]=บริการสอดส่องการเปลี่ยนแปลงของแฟ้มสำหรับ Neomuk
+Comment[tr]=Nepomuk dosya izleme servisi
+Comment[ug]=Nepomuk ھۆججەت نازارەت مۇلازىمىتى
+Comment[uk]=Служба нагляду за файлами Nepomuk
+Comment[x-test]=xxNepomuk file watch servicexx
+Comment[zh_CN]=Nepomuk 文件监视服务
+Comment[zh_TW]=Nepomuk 檔案監控服務
+
+[Event/nepomuk_new_removable_device]
+Name=New Removable Device
+Name[bg]=Ново преносимо устройство
+Name[bs]=Uklonjivi uređaji
+Name[ca]=Dispositiu extraïble nou
+Name[ca@valencia]=Dispositiu extraïble nou
+Name[cs]=Nové odpojitelné zařízení
+Name[da]=Ny flytbar enhed
+Name[de]=Neues Wechselmedium
+Name[es]=Nuevo dispositivo extraíble
+Name[et]=Uus eemaldatav seade
+Name[eu]=Gailu aldagarri berria
+Name[fi]=Uusi irrotettava laite
+Name[fr]=Nouveau périphérique amovible
+Name[he]=התקן נשלף חדש
+Name[hr]=Novi uklonjivi uređaji
+Name[hu]=Új cserélhető eszközök
+Name[ia]=Nove dispositivo removibile
+Name[is]=Nýtt útskiptanlegt tæki
+Name[it]=Nuovo dispositivo rimovibile
+Name[kk]=Жаңа ауыстырмалы құрылғы
+Name[km]=ឧបករណ៍ចល័ត​ថ្មី
+Name[kn]=ಹೊಸ ತೆಗೆದುಹಾಕಬಹುದಾದ ಸಾಧನಗಳು
+Name[ko]=새 이동식 장치
+Name[lt]=Naujas pašalinamas įrenginys
+Name[lv]=Jauna noņemamā iekārta
+Name[nb]=Ny flyttbar enhet
+Name[nds]=Niege tuuschbore Reedschappen
+Name[nl]=Nieuwe verwijderbare apparaten
+Name[nn]=Ny flyttbar eining
+Name[pa]=ਨਵਾਂ ਹਟਾਉਣਯੋਗ ਜੰਤਰ
+Name[pl]=Nowe urządzenie wymienne
+Name[pt]=Novo Dispositivo Removível
+Name[pt_BR]=Novo dispositivo removível
+Name[ro]=Dispozitiv amovibil nou
+Name[ru]=Новое внешнее устройство
+Name[sl]=Nova odstranljiva naprava
+Name[sr]=Нови уклоњиви уређај
+Name[sr@ijekavian]=Нови уклоњиви уређај
+Name[sr@ijekavianlatin]=Novi uklonjivi uređaj
+Name[sr@latin]=Novi uklonjivi uređaj
+Name[sv]=Flyttbara enheter
+Name[th]=อุปกรณ์ที่สามารถถอด/เสียบได้ตัวใหม่
+Name[tr]=Yeni Çıkarılabilir Aygıt
+Name[ug]=يېڭى يۆتكىلىشچان ئۈسكۈنە
+Name[uk]=Новий портативний пристрій
+Name[x-test]=xxNew Removable Devicexx
+Name[zh_CN]=新移动设备
+Name[zh_TW]=新的可移除裝置
+Comment=A new unknown removable device has been mounted
+Comment[bg]=Монтирано е ново непознато преносимо устройство
+Comment[bs]=Novi nepoznati uklonjivi uređaj je montiran
+Comment[ca]=S'ha muntat un dispositiu extraïble desconegut nou
+Comment[ca@valencia]=S'ha muntat un dispositiu extraïble desconegut nou
+Comment[cs]=Bylo připojeno nové neznámé odpojitelné zařízeni
+Comment[da]=En ny ukendt flytbar enhed er blevet monteret
+Comment[de]=Ein neues, unbekanntes Wechselmedium ist eingebunden worden
+Comment[es]=Se ha montado un nuevo dispositivo extraíble desconocido
+Comment[et]=Ühendati uus tundmatu eemaldatav seade
+Comment[eu]=Gailu aldagarri berri ezezagun bat muntatu da
+Comment[fi]=Liitettiin uusi tuntematon irrotettava laite
+Comment[fr]=Un nouveau périphérique amovible inconnu a été monté
+Comment[he]=חובר התקן חדש, לא מוכר
+Comment[hr]=Montiran je novi nepoznati uklonjivi uređaj
+Comment[hu]=Új, ismeretlen cserélhető eszköz került csatolásra
+Comment[ia]=Un nove dispositivo removibile incognite ha essite montate
+Comment[is]=Nútt óþekkt útskiptanlegt tæki hefur verið tengt
+Comment[it]=È stato montato un nuovo dispositivo rimovibile sconosciuto
+Comment[kk]=Жаңа беймәлім ауыстырмалы құрылғы тіркеуден өтті
+Comment[km]=ឧបករណ៍​ចល័ត​ដែល​មិន​ស្គាល់​ថ្មី​ត្រូវ​បាន​ម៉ោន
+Comment[ko]=새 이동식 장치가 마운트됨
+Comment[lt]=Prijungtas naujas nežinomas pašalinamas įrenginys
+Comment[lv]=Tika piemontēta jauna, nezināma noņemamā ierīce
+Comment[nb]=En ny ukjent flyttbar enhet er montert
+Comment[nds]=En nieg nich begäng tuuschbor Reedschap wöör inhangt
+Comment[nl]=Een nieuw verwijderbaar apparaat is aangekoppeld
+Comment[nn]=Ei ny ukjend flyttbar eining er no montert
+Comment[pa]=ਇੱਕ ਨਵਾਂ ਅਣਜਾਣ ਹਟਾਉਣਯੋਗ ਜੰਤਰ ਮਾਊਂਟ ਕੀਤਾ ਗਿਆ ਹੈ
+Comment[pl]=Nowe nieznane urządzenie wymienne zostało zamontowane
+Comment[pt]=Foi montado um novo dispositivo removível desconhecido
+Comment[pt_BR]=Foi montado um novo dispositivo removível desconhecido
+Comment[ro]=Un dispozitiv amovibil necunoscut nou a fost montat
+Comment[ru]=Подключено новое, ранее неизвестное внешнее устройство
+Comment[sl]=Priklopljena je bila nova neznana odstranljiva baprava
+Comment[sr]=Монтиран је непознат нови уклоњиви уређај
+Comment[sr@ijekavian]=Монтиран је непознат нови уклоњиви уређај
+Comment[sr@ijekavianlatin]=Montiran je nepoznat novi uklonjivi uređaj
+Comment[sr@latin]=Montiran je nepoznat novi uklonjivi uređaj
+Comment[sv]=En ny obekant flyttbar enhet har monterats
+Comment[th]=มีการเมานท์อุปกรณ์ที่สามารถถอด/เสียบได้ตัวใหม่ที่ไม่รู้จักแล้ว
+Comment[tr]=Yeni bir bilinmeyen çıkarılabilir aygıt bağlandı
+Comment[ug]=يېڭى بىر يوچۇن يۆتكىلىشچان ئۈسكۈنە ئېگەرلەندى
+Comment[uk]=Було змонтовано новий невідомий портативний пристрій
+Comment[x-test]=xxA new unknown removable device has been mountedxx
+Comment[zh_CN]=已经挂载了一个新移动设备
+Comment[zh_TW]=有一個新的可移除裝置已經被掛載了
+Action=Popup
diff --git a/nepomuk/services/filewatch/removabledeviceindexnotification.cpp b/nepomuk/services/filewatch/removabledeviceindexnotification.cpp
new file mode 100644
index 0000000..fa9ade8
--- /dev/null
+++ b/nepomuk/services/filewatch/removabledeviceindexnotification.cpp
@@ -0,0 +1,99 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "removabledeviceindexnotification.h"
+#include "strigiserviceinterface.h"
+
+#include <KLocale>
+#include <KConfig>
+#include <KConfigGroup>
+#include <KIcon>
+#include <KDebug>
+#include <KToolInvocation>
+
+#include <Solid/StorageAccess>
+
+RemovableDeviceIndexNotification::RemovableDeviceIndexNotification(const Nepomuk::RemovableMediaCache::Entry* medium,
+                                                                   QObject *parent)
+    : KNotification(QLatin1String("nepomuk_new_removable_device"),
+          KNotification::Persistent,
+          parent),
+      m_medium(medium)
+{
+    setTitle(i18nc("@title", "New removable device detected"));
+    setText(i18nc("@info", "Do you want files on removable device <resource>%1</resource> to be indexed for fast desktop searches?", m_medium->device().description()));
+    setPixmap(KIcon(QLatin1String("nepomuk")).pixmap(32, 32));
+
+    setActions(QStringList()
+               << i18nc("@action", "Index files")
+               << i18nc("@action", "Ignore device")
+               << i18nc("@action", "Configure"));
+    connect(this, SIGNAL(activated(uint)), this, SLOT(slotActionActivated(uint)));
+
+    // as soon as the device is unmounted this notification becomes pointless
+    if ( const Solid::StorageAccess* storage = m_medium->device().as<Solid::StorageAccess>() ) {
+        connect(storage, SIGNAL(accessibilityChanged(bool,QString)), SLOT(close()));
+    }
+}
+
+void RemovableDeviceIndexNotification::slotActionDoIndexActivated()
+{
+    KConfig strigiConfig( "nepomukstrigirc" );
+    strigiConfig.group("Devices").writeEntry(m_medium->url(), true);
+
+    org::kde::nepomuk::Strigi strigi( "org.kde.nepomuk.services.nepomukstrigiservice", "/nepomukstrigiservice", QDBusConnection::sessionBus() );
+    strigi.indexFolder( m_medium->mountPath(), true /* recursive */, false /* no forced update */ );
+
+    close();
+}
+
+void RemovableDeviceIndexNotification::slotActionDoNotIndexActivated()
+{
+    KConfig strigiConfig( "nepomukstrigirc" );
+    strigiConfig.group("Devices").writeEntry(m_medium->url(), false);
+
+    close();
+}
+
+void RemovableDeviceIndexNotification::slotActionConfigureActivated()
+{
+    QStringList args;
+    args << "kcm_nepomuk" << "--args" << "1";
+    KToolInvocation::kdeinitExec("kcmshell4", args);
+}
+
+void RemovableDeviceIndexNotification::slotActionActivated(uint action)
+{
+    kDebug() << action;
+    switch(action) {
+    case 1:
+        slotActionDoIndexActivated();
+        break;
+    case 2:
+        slotActionDoNotIndexActivated();
+        break;
+    case 3:
+        slotActionConfigureActivated();
+        break;
+    }
+}
+
+#include "removabledeviceindexnotification.moc"
diff --git a/nepomuk/services/filewatch/removabledeviceindexnotification.h b/nepomuk/services/filewatch/removabledeviceindexnotification.h
new file mode 100644
index 0000000..e0b1dae
--- /dev/null
+++ b/nepomuk/services/filewatch/removabledeviceindexnotification.h
@@ -0,0 +1,48 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef REMOVABLEDEVICEINDEXNOTIFICATION_H
+#define REMOVABLEDEVICEINDEXNOTIFICATION_H
+
+#include "removablemediacache.h"
+
+#include <KNotification>
+
+#include <Solid/Device>
+
+class RemovableDeviceIndexNotification : public KNotification
+{
+    Q_OBJECT
+
+public:
+    RemovableDeviceIndexNotification(const Nepomuk::RemovableMediaCache::Entry* medium, QObject *parent = 0);
+
+private slots:
+    void slotActionActivated(uint action);
+    void slotActionDoIndexActivated();
+    void slotActionDoNotIndexActivated();
+    void slotActionConfigureActivated();
+
+private:
+    const Nepomuk::RemovableMediaCache::Entry* m_medium;
+};
+
+#endif // REMOVABLEDEVICEINDEXNOTIFICATION_H
diff --git a/nepomuk/services/queryservice/folder.cpp b/nepomuk/services/queryservice/folder.cpp
index 030fe06..8596498 100644
--- a/nepomuk/services/queryservice/folder.cpp
+++ b/nepomuk/services/queryservice/folder.cpp
@@ -30,6 +30,7 @@
 #include <KDebug>
 
 #include <QtCore/QThreadPool>
+#include <QtCore/QMutexLocker>
 
 
 Nepomuk::Query::Folder::Folder( const Query& query, QObject* parent )
@@ -37,7 +38,8 @@ Nepomuk::Query::Folder::Folder( const Query& query, QObject* parent )
       m_isSparqlQueryFolder( false ),
       m_query( query ),
       m_currentSearchRunnable( 0 ),
-      m_currentCountQueryRunnable( 0 )
+      m_currentCountQueryRunnable( 0 ),
+      m_runnableMutex(QMutex::Recursive)
 {
     init();
 }
@@ -49,7 +51,8 @@ Nepomuk::Query::Folder::Folder( const QString& query, const RequestPropertyMap&
       m_sparqlQuery( query ),
       m_requestProperties( requestProps ),
       m_currentSearchRunnable( 0 ),
-      m_currentCountQueryRunnable( 0 )
+      m_currentCountQueryRunnable( 0 ),
+      m_runnableMutex(QMutex::Recursive)
 {
     init();
 }
@@ -75,6 +78,7 @@ void Nepomuk::Query::Folder::init()
 
 Nepomuk::Query::Folder::~Folder()
 {
+    QMutexLocker lock(&m_runnableMutex);
     if( m_currentSearchRunnable )
         m_currentSearchRunnable->cancel();
     if( m_currentCountQueryRunnable )
@@ -88,6 +92,7 @@ Nepomuk::Query::Folder::~Folder()
 
 void Nepomuk::Query::Folder::update()
 {
+    QMutexLocker lock(&m_runnableMutex);
     if ( !m_currentSearchRunnable ) {
         m_currentSearchRunnable = new SearchRunnable( this );
         QueryService::searchThreadPool()->start( m_currentSearchRunnable, 1 );
@@ -106,12 +111,14 @@ void Nepomuk::Query::Folder::update()
 
 QList<Nepomuk::Query::Result> Nepomuk::Query::Folder::entries() const
 {
-    return m_results.toList();
+    QMutexLocker lock(&m_runnableMutex);
+    return m_results.values();
 }
 
 
 bool Nepomuk::Query::Folder::initialListingDone() const
 {
+    QMutexLocker lock(&m_runnableMutex);
     return m_initialListingDone;
 }
 
@@ -137,22 +144,23 @@ Nepomuk::Query::RequestPropertyMap Nepomuk::Query::Folder::requestPropertyMap()
 // called from SearchRunnable in the search thread
 void Nepomuk::Query::Folder::addResults( const QList<Nepomuk::Query::Result>& results )
 {
+    QMutexLocker lock(&m_runnableMutex);
+
     QSet<Result> newResults;
     Q_FOREACH( const Result& result, results ) {
-        if ( !m_results.contains( result ) ) {
+        if ( !m_results.contains( result.resource().resourceUri() ) ) {
             newResults.insert( result );
         }
     }
 
-    if ( m_initialListingDone ) {
-        m_newResults += QSet<Result>::fromList(results);
-    }
-    else {
-        m_results += QSet<Result>::fromList(results);
+    Q_FOREACH(const Result& result, results) {
+        if ( !m_newResults.contains( result.resource().resourceUri() ) ) {
+            m_newResults.insert(result.resource().resourceUri(), result);
+        }
     }
 
     if( !newResults.isEmpty() ) {
-        emit newEntries( newResults.toList() );
+        emit newEntries( newResults.values() );
     }
 }
 
@@ -160,21 +168,31 @@ void Nepomuk::Query::Folder::addResults( const QList<Nepomuk::Query::Result>& re
 // called from SearchRunnable in the search thread
 void Nepomuk::Query::Folder::listingFinished()
 {
+    QMutexLocker lock(&m_runnableMutex);
+
     m_currentSearchRunnable = 0;
 
-    if ( m_initialListingDone ) {
-        // inform about removed items
-        foreach( const Result& result, m_results ) {
-            if ( !m_newResults.contains( result ) ) {
-                emit entriesRemoved( QList<QUrl>() << result.resource().resourceUri() );
-            }
+    // inform about removed items
+    QList<Result> removedResults;
+
+    // legacy removed results
+    foreach( const Result& result, m_results ) {
+        if ( !m_newResults.contains( result.resource().resourceUri() ) ) {
+            removedResults << result;
+            emit entriesRemoved( QList<QUrl>() << KUrl(result.resource().resourceUri()).url() );
         }
+    }
 
-        // reset
-        m_results = m_newResults;
-        m_newResults.clear();
+    // new removed results which include all the details to be used for optimizations
+    if( !removedResults.isEmpty() ) {
+        emit entriesRemoved( removedResults );
     }
-    else {
+
+    // reset
+    m_results = m_newResults;
+    m_newResults.clear();
+
+    if ( !m_initialListingDone ) {
         kDebug() << "Listing done. Total:" << m_results.count();
         m_initialListingDone = true;
         emit finishedListing();
@@ -189,6 +207,7 @@ void Nepomuk::Query::Folder::listingFinished()
 
 void Nepomuk::Query::Folder::slotStorageChanged()
 {
+    QMutexLocker lock(&m_runnableMutex);
     if ( !m_updateTimer.isActive() && !m_currentSearchRunnable ) {
         update();
     }
@@ -201,6 +220,7 @@ void Nepomuk::Query::Folder::slotStorageChanged()
 // if there was a change in the nepomuk store we update
 void Nepomuk::Query::Folder::slotUpdateTimeout()
 {
+    QMutexLocker lock(&m_runnableMutex);
     if ( m_storageChanged && !m_currentSearchRunnable ) {
         m_storageChanged = false;
         update();
@@ -211,6 +231,8 @@ void Nepomuk::Query::Folder::slotUpdateTimeout()
 // called from CountQueryRunnable in the search thread
 void Nepomuk::Query::Folder::countQueryFinished( int count )
 {
+    QMutexLocker lock(&m_runnableMutex);
+
     m_currentCountQueryRunnable = 0;
 
     m_resultCount = count;
diff --git a/nepomuk/services/queryservice/folder.h b/nepomuk/services/queryservice/folder.h
index b7d2727..7a579b5 100644
--- a/nepomuk/services/queryservice/folder.h
+++ b/nepomuk/services/queryservice/folder.h
@@ -27,6 +27,7 @@
 #include <QtCore/QSet>
 #include <QtCore/QTimer>
 #include <QtCore/QPointer>
+#include <QtCore/QMutex>
 
 #include <KUrl>
 
@@ -105,6 +106,7 @@ namespace Nepomuk {
         Q_SIGNALS:
             void newEntries( const QList<Nepomuk::Query::Result>& entries );
             void entriesRemoved( const QList<QUrl>& entries );
+            void entriesRemoved( const QList<Nepomuk::Query::Result>& entries );
 
             /**
              * Emitted once the result count is available.
@@ -158,14 +160,15 @@ namespace Nepomuk {
             bool m_initialListingDone;
 
             /// the actual current results
-            QSet<Result> m_results;
+            QHash<QUrl, Result> m_results;
 
             /// the results gathered during an update, needed to find removed items
-            QSet<Result> m_newResults;
+            QHash<QUrl, Result> m_newResults;
 
             /// the runnable doing work at the moment or 0 if idle
             SearchRunnable* m_currentSearchRunnable;
             CountQueryRunnable* m_currentCountQueryRunnable;
+            mutable QMutex m_runnableMutex;
 
             /// did the nepomuk store change after the last update - used for caching of update signals via m_updateTimer
             bool m_storageChanged;
diff --git a/nepomuk/services/queryservice/folderconnection.cpp b/nepomuk/services/queryservice/folderconnection.cpp
index a47438e..28bc681 100644
--- a/nepomuk/services/queryservice/folderconnection.cpp
+++ b/nepomuk/services/queryservice/folderconnection.cpp
@@ -20,6 +20,9 @@
 #include "folder.h"
 #include "queryadaptor.h"
 
+#include <nepomuk/resource.h>
+#include <nepomuk/result.h>
+
 #include <QtCore/QStringList>
 #include <QtDBus/QDBusServiceWatcher>
 #include <QtDBus/QDBusConnection>
@@ -47,8 +50,8 @@ void Nepomuk::Query::FolderConnection::list()
     m_folder->disconnect( this );
     connect( m_folder, SIGNAL( newEntries( QList<Nepomuk::Query::Result> ) ),
              this, SIGNAL( newEntries( QList<Nepomuk::Query::Result> ) ) );
-    connect( m_folder, SIGNAL( entriesRemoved( QList<QUrl> ) ),
-             this, SLOT( slotEntriesRemoved( QList<QUrl> ) ) );
+    connect( m_folder, SIGNAL( entriesRemoved( QList<Nepomuk::Query::Result> ) ),
+             this, SLOT( slotEntriesRemoved( QList<Nepomuk::Query::Result> ) ) );
 
     // report cached entries
     if ( !m_folder->entries().isEmpty() ) {
@@ -88,8 +91,8 @@ void Nepomuk::Query::FolderConnection::listen()
     if ( m_folder->initialListingDone() ) {
         connect( m_folder, SIGNAL( newEntries( QList<Nepomuk::Query::Result> ) ),
                  this, SIGNAL( newEntries( QList<Nepomuk::Query::Result> ) ) );
-        connect( m_folder, SIGNAL( entriesRemoved( QList<QUrl> ) ),
-                 this, SLOT( slotEntriesRemoved( QList<QUrl> ) ) );
+        connect( m_folder, SIGNAL( entriesRemoved( QList<Nepomuk::Query::Result> ) ),
+                 this, SLOT( slotEntriesRemoved( QList<Nepomuk::Query::Result> ) ) );
         connect( m_folder, SIGNAL( resultCount( int ) ),
                  this, SIGNAL( resultCount( int ) ) );
     }
@@ -102,13 +105,14 @@ void Nepomuk::Query::FolderConnection::listen()
 }
 
 
-void Nepomuk::Query::FolderConnection::slotEntriesRemoved( const QList<QUrl>& entries )
+void Nepomuk::Query::FolderConnection::slotEntriesRemoved( const QList<Nepomuk::Query::Result>& entries )
 {
     QStringList uris;
     for ( int i = 0; i < entries.count(); ++i ) {
-        uris.append( entries[i].toString() );
+        uris.append( entries[i].resource().resourceUri().toString() );
     }
     emit entriesRemoved( uris );
+    emit entriesRemoved( entries );
 }
 
 
@@ -118,8 +122,8 @@ void Nepomuk::Query::FolderConnection::slotFinishedListing()
     // finished we can start listening for changes
     connect( m_folder, SIGNAL( newEntries( QList<Nepomuk::Query::Result> ) ),
              this, SIGNAL( newEntries( QList<Nepomuk::Query::Result> ) ) );
-    connect( m_folder, SIGNAL( entriesRemoved( QList<QUrl> ) ),
-             this, SLOT( slotEntriesRemoved( QList<QUrl> ) ) );
+    connect( m_folder, SIGNAL( entriesRemoved( QList<Nepomuk::Query::Result> ) ),
+             this, SLOT( slotEntriesRemoved( QList<Nepomuk::Query::Result> ) ) );
 }
 
 
diff --git a/nepomuk/services/queryservice/folderconnection.h b/nepomuk/services/queryservice/folderconnection.h
index df75b2b..835cb9d 100644
--- a/nepomuk/services/queryservice/folderconnection.h
+++ b/nepomuk/services/queryservice/folderconnection.h
@@ -62,12 +62,15 @@ namespace Nepomuk {
         Q_SIGNALS:
             void newEntries( const QList<Nepomuk::Query::Result>& );
             void entriesRemoved( const QStringList& );
+            void entriesRemoved( const QList<Nepomuk::Query::Result>& entries );
+
             void resultCount( int count );
             void totalResultCount( int count );
             void finishedListing();
 
         private Q_SLOTS:
-            void slotEntriesRemoved( const QList<QUrl>& entries );
+            //void slotNewEntries( const QList<Nepomuk::Query::Result>& results );
+            void slotEntriesRemoved( const QList<Nepomuk::Query::Result>& entries );
             void slotFinishedListing();
 
         private:
diff --git a/nepomuk/services/queryservice/nepomukqueryservice.desktop b/nepomuk/services/queryservice/nepomukqueryservice.desktop
index 7c1e2df..1fd6311 100644
--- a/nepomuk/services/queryservice/nepomukqueryservice.desktop
+++ b/nepomuk/services/queryservice/nepomukqueryservice.desktop
@@ -11,6 +11,7 @@ Name[be@latin]=Słužba „NepomukQueryService”
 Name[bg]=NepomukQueryService
 Name[bn]=NepomukQueryService
 Name[bn_IN]=NepomukQueryService
+Name[bs]=Servis Nepomukovih upita
 Name[ca]=NepomukQueryService
 Name[ca@valencia]=NepomukQueryService
 Name[cs]=Dotazovací služba Nepomuku
@@ -74,6 +75,7 @@ Name[ta]=NepomukQueryService
 Name[tg]=Хидматҳои Nepomuk
 Name[th]=NepomukQueryService
 Name[tr]=Nepomuk Sorgulama Servisi
+Name[ug]=Nepomuk سۈرۈشتۈرۈش مۇلازىمىتى
 Name[uk]=NepomukQueryService
 Name[wa]=SierviceCweraedjeNepomuk
 Name[x-test]=xxNepomukQueryServicexx
@@ -83,6 +85,8 @@ Comment=The Nepomuk Query Service provides an interface for persistent query fol
 Comment[ar]=تقدم خدمة استعلام نبومك واجهة لمجلدات الاستعلام الموجودة مسبقا.
 Comment[ast]=El serviciu de consultes de Nepomuk ufre una interface pa consultes a carpetes persistentes
 Comment[be@latin]=Słužba zapytaŭ „Nepomuk” absłuhoŭvaje interfejs dla stałych katalohaŭ, jakija biaruć źviestki ad zapytaŭ.
+Comment[bg]=Услуга на Nepomuk, която позволява запазване на търсене като директория
+Comment[bs]=Servis Nepomukovih upita pruža sučelje za trajne upitne fascikle
 Comment[ca]=El servei de consultes del Nepomuk proporciona una interfície per a carpetes persistents de consultes
 Comment[ca@valencia]=El servei de consultes del Nepomuk proporciona una interfície per a carpetes persistents de consultes
 Comment[cs]=Dotazovací služba Nepomuku poskytuje rozhraní pro složky trvalých dotazů
@@ -105,7 +109,7 @@ Comment[hi]=नेपोमक क्वैरी सेवा जो परस
 Comment[hr]=Nepomukova usluga upita omogućuje sučelje za trajne upitne direktorije
 Comment[hsb]=Nepomukowa naprašowanska słužba staji intefejs za persistentne naprašowanske zapiski k dispoziciji.
 Comment[hu]=A Nepomuk lekérdező szolgáltatás keresőmappákhoz nyújt felületet
-Comment[ia]=Le Nepomuk Query Service forni un interface pro dossieres persistente de demanda
+Comment[ia]=Le Nepomuk Query Service forni un interface pro dossieres de demanda persistente
 Comment[id]=Layanan Tanya Nepomuk menyediakan antarmuka untuk folder pertanyaan yang kukuh
 Comment[is]=Nepomuk fyrirspurnamiðlarinn er viðmót fyrir viðvarandi fyrirspurnamöppur (persistent query folders)
 Comment[it]=Il servizio di interrogazione di Nepomuk fornisce un'interfaccia per cartelle di interrogazione persistenti
@@ -142,6 +146,7 @@ Comment[ta]=The Nepomuk Query Service provides an interface for persistent query
 Comment[te]=నెపోమక్ క్వరీ సేవ అనునది ఎప్పుడూవుండే క్వరీ ఫోల్డర్సుకు ఇంటర్ఫేస్‌ను అందిస్తుంది
 Comment[th]=บริการสืบค้น Nepomuk เป็นส่วนติดต่อสำหรับโฟลเดอร์สืบค้นข้อมูลถาวร
 Comment[tr]=Nepomuk Sorgulama Servisi kalıcı sorgu klasörleri için bir arayüz sağlar
+Comment[ug]=Nepomuk سۈرۈشتۈرۈش مۇلازىمىتى بىر خىل ئىزچىل بولغان ھۆججەت سۈرۈشتۈرۈش ئېغىزى بىلەن تەمىنلىدى
 Comment[uk]=Служба запитів Nepomuk надає інтерфейс для постійних тек запитів
 Comment[wa]=Li siervice di cweraedje di Nepomuk dene èn eterface po des ridants d' cweraedje wårdés
 Comment[x-test]=xxThe Nepomuk Query Service provides an interface for persistent query foldersxx
diff --git a/nepomuk/services/removablestorage/CMakeLists.txt b/nepomuk/services/removablestorage/CMakeLists.txt
deleted file mode 100644
index 07d19b8..0000000
--- a/nepomuk/services/removablestorage/CMakeLists.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-project(nepomukremovablestorageservice)
-
-include_directories(
-  ${QT_INCLUDES}
-  ${KDE4_INCLUDES}
-  ${SOPRANO_INCLUDE_DIR}
-  ${CMAKE_SOURCE_DIR}
-  ${NEPOMUK_INCLUDE_DIR}
-  )
-
-set(SRCS
-  removablestorageservice.cpp
-  )
-
-qt4_add_dbus_interface(SRCS ../../interfaces/org.kde.nepomuk.Strigi.xml strigiserviceinterface)
-qt4_add_dbus_interface(SRCS ../../interfaces/org.kde.nepomuk.FileWatch.xml filewatchserviceinterface)
-
-kde4_add_plugin(nepomukremovablestorageservice ${SRCS})
-
-target_link_libraries(nepomukremovablestorageservice
-  nepomukcommon
-  ${SOPRANO_CLIENT_LIBRARIES}
-  ${SOPRANO_LIBRARIES}
-  ${KDE4_KDEUI_LIBS}
-  ${KDE4_KIO_LIBS}
-  ${NEPOMUK_LIBRARIES}
-  ${KDE4_SOLID_LIBS}
-  )
-
-install(
-  FILES nepomukremovablestorageservice.desktop
-  DESTINATION ${SERVICES_INSTALL_DIR})
-install(
-  TARGETS nepomukremovablestorageservice
-  DESTINATION ${PLUGIN_INSTALL_DIR})
diff --git a/nepomuk/services/removablestorage/nepomukremovablestorageservice.desktop b/nepomuk/services/removablestorage/nepomukremovablestorageservice.desktop
deleted file mode 100644
index e2bba8c..0000000
--- a/nepomuk/services/removablestorage/nepomukremovablestorageservice.desktop
+++ /dev/null
@@ -1,124 +0,0 @@
-[Desktop Entry]
-Type=Service
-ServiceTypes=NepomukService
-X-KDE-Library=nepomukremovablestorageservice
-X-KDE-Nepomuk-autostart=true
-X-KDE-Nepomuk-start-on-demand=false
-Name=Nepomuk Removable Storage Service
-Name[ar]=خدمة نبومك للتخزين القابل للإزالة
-Name[ast]=Serviciu d'almacenamientu estrayíble de Nepomuk
-Name[ca]=Servei d'emmagatzematge extraïble del Nepomuk
-Name[ca@valencia]=Servei d'emmagatzematge extraïble del Nepomuk
-Name[cs]=Služba odpojitelných úložišť Nepomuku
-Name[csb]=Ùsłëznota Nepomuka dlô przenosnych mediów
-Name[da]=Nepomuk-tjeneste til flytbare lagringsenheder
-Name[de]=Nepomuk-Dienst für Wechselmedien
-Name[el]=Υπηρεσία αφαιρούμενων μέσων αποθήκευσης του Nepomuk
-Name[en_GB]=Nepomuk Removable Storage Service
-Name[es]=Servicio de almacenamiento extraíble de Nepomuk
-Name[et]=Nepomuki eemaldatava salvesti teenus
-Name[eu]=Nepomuk biltegiratzeko aldagarrien zerbitzua
-Name[fi]=Nepomukin irrotettava tallennuspalvelu
-Name[fr]=Service de stockage amovible de Nepomuk
-Name[fy]=Nepomuk opslach tsjinst
-Name[ga]=Seirbhís Stórála Inbhainte Nepomuk
-Name[he]=שירות התקני אחסון נשלפים של Nepomuk
-Name[hi]=नेपोमक हटानेयोग्य भंडार सेवा
-Name[hr]=Nepomukova usluga za uklonjivo skladište
-Name[hu]=Nepomuk cserélhető adathordozó szolgáltatás
-Name[ia]=Servicio de Nepomuk de immagazinage (Storage )Removibile
-Name[id]=Layanan Penyimpanan Dapat Dilepas Nepomuk
-Name[is]=Nepomuk miðlari fyrir aftengjanlegar gagnageymslur
-Name[it]=Servizio per supporti rimovibili di Nepomuk
-Name[ja]=Nepomuk リムーバブルストレージサービス
-Name[kk]=Ауыстырмалы Nepomuk құрылғыда сақтау қызметі
-Name[km]=សេវា​ផ្ទុក​ចល័រ​របស់ Nepomuk
-Name[kn]=ನೆಪೋಮುಕ್ ತೆಗೆದು ಹಾಕಬಹುದಾದ ಶೇಖರಣಾ ಸೇವೆ
-Name[ko]=Nepomuk 이동식 저장소 서비스
-Name[lt]=Nepomuk atjungiamų laikmenų paslauga
-Name[lv]=Nepomuk noņemamo glabāšanas ierīču serviss
-Name[mai]=नेपोमक हटानेयोग्य भंडार सेवा
-Name[mk]=Сервис на Непомук за подвижни медиуми
-Name[ml]=നെപ്പോമുക്ക്  സൂക്ഷിപ്പു് സേവനം
-Name[nb]=Nepomuk flyttbar lagringstjeneste
-Name[nds]=Nepomuk-Tuuschbor-Loopwark-Deenst
-Name[nl]=Nepomuk verwijderbare opslagdienst
-Name[nn]=Nepomuk-teneste for lagringsmedium
-Name[pa]=ਨਿਪੋਮੁਕ ਹਟਾਉਣਯੋਗ ਸਟੋਰੇਜ਼ ਸਰਵਿਸ
-Name[pl]=Usługa mediów wymiennych Nepomuka
-Name[pt]=Serviço de Armazenamento Removível do Nepomuk
-Name[pt_BR]=Serviço de armazenamento removível do Nepomuk
-Name[ro]=Serviciu Nepomuk de stocare detașabilă
-Name[ru]=Служба хранилища Nepomuk на съёмных носителях
-Name[si]=Nepomuk ඉවත්කල හැකි ගබඩා සේවාව
-Name[sk]=Služba pre vymeniteľné úložiská Nepomuku
-Name[sl]=Storitev odstranljivih nosilcev za Nepomuk
-Name[sr]=Непомуков сервис уклоњивих складишта
-Name[sr@ijekavian]=Непомуков сервис уклоњивих складишта
-Name[sr@ijekavianlatin]=Nepomukov servis uklonjivih skladišta
-Name[sr@latin]=Nepomukov servis uklonjivih skladišta
-Name[sv]=Nepomuk-tjänst för flyttbara medier
-Name[tg]=Хидмати захирагоҳи Nepomuk
-Name[th]=บริการเก็บข้อมูล Nepomuk บนสื่อถอด/เสียบได้
-Name[tr]=Nepomuk Çıkarılabilir Depolama Servisi
-Name[uk]=Служба зберігання на портативних пристроях Nepomuk
-Name[wa]=Li siervice des éndjins di stocaedje bodjåves di Nepomuk
-Name[x-test]=xxNepomuk Removable Storage Servicexx
-Name[zh_CN]=Nepomuk 移动存储服务
-Name[zh_TW]=Nepomuk 可移除儲存服務
-Comment=The Nepomuk removable storage service, providing access to Nepomuk metadata on removable storage devices.
-Comment[ar]=خدمة نِبوموك للتخزين القابل للإزالة, توفر الوصول إلى البيانات العامة لنِبوموك على أجهزة التخزين القابلة للإزالة
-Comment[ast]=El serviciu d'atroxamientu estrayíble de Nepomuk, ufre acesu a metadatos de Nepomuk en preseos d'atroxamientu estrayibles.
-Comment[ca]=El servei d'emmagatzematge extraïble del Nepomuk, proporciona accés a les metadades del Nepomuk en dispositius d'emmagatzematge extraïbles.
-Comment[ca@valencia]=El servei d'emmagatzematge extraïble del Nepomuk, proporciona accés a les metadades del Nepomuk en dispositius d'emmagatzematge extraïbles.
-Comment[cs]=Služba odpojitelných úložišť Nepomuku poskytuje přístup pro metadata Nepomuku na odpojitelných zařízeních.
-Comment[csb]=Ùsłëznota Nepomuka dlô przenoslnych mediów dôwô przëstãp do pòdôwków meta Nepomùka na wëmienialnych mediach.
-Comment[da]=Nepomuk-tjenesten til flytbare lagringsenheder giver adgang til Nepomuk-metadata på flytbare lagringsenheder.
-Comment[de]=Der Nepomuk-Dienst für Wechselmedien, ermöglicht Zugriff auf Nepomuk-Metadaten auf Wechselmedien.
-Comment[el]=Η υπηρεσία αφαιρούμενων μέσων αποθήκευσης του Nepomuk, παρέχει πρόσβαση στα μεταδεδομένα του Nepomuk σε αφαιρούμενες συσκευές αποθήκευσης.
-Comment[en_GB]=The Nepomuk removable storage service, providing access to Nepomuk metadata on removable storage devices.
-Comment[es]=El servicio de almacenamiento extraíble de Nepomuk, que proporciona acceso a metadatos de Nepomuk en dispositivos de almacenamiento extraíbles.
-Comment[et]=Nepomuki eemaldatava salvesti teenus, mis võimaldab kasutada Nepomuki metaandmeid eemaldataval salvestusseadmel.
-Comment[eu]=Nepomuken biltegiratzeko aldagarrien zerbitzua, Nepomuken metadatuei sarbidea ematen die biltegiratzeko gailu aldagarrietan.
-Comment[fi]=Nepomuk-irrotettavien tallennuslaitteiden palvelu tarjoaa pääsyn Nepomuk-metadataan irrotettavilla tallennuslaitteilla.
-Comment[fr]=Le service de stockage amovible de Nepomuk, fournissant les méta-données de Nepomuk sur les périphériques de stockage amovibles.
-Comment[fy]=De Nepomuk útnimber opslach tsjinst, ferskaft troch de Nepomuk metadata op útnimber opslach apparaten.
-Comment[he]=שירות התקני אחסון נשלפים של Nepomuk, מספק גישה למידע של Nepomuk עבור התקני אחסון נשלפים.
-Comment[hr]=Nepomukova usluga uklonjivih uređaja za pohranu omogućuje pristup Nepomukovim metapodacima na uklonjivim uređajima za pohranu.
-Comment[hu]=A Nepomuk cserélhető adathordozó szolgáltatás hozzáférést biztosít a cserélhető adathordozókon található metaadatokhoz.
-Comment[ia]=Le servicio de immagazinage removibile de Nepomuk forni accesso a metadata de Nepomuk su dispositivos de immagazinage removibile.
-Comment[id]=Layanan penyimpanan dapat dilepas Nepomuk, memberikan akses ke metadata Nepomuk di divais penyimpanan dapat dilepas.
-Comment[is]=Nepomuk miðlari fyrir aftengjanlegar gagnageymslur, gerir kleift að nálgast Nepomuk lýsigögn á aftengjanlegum gagnageymslum.
-Comment[it]=Il servizio per supporti rimovibili di Nepomuk, che dà accesso ai dati aggiuntivi di Nepomuk sui supporti rimovibili.
-Comment[ja]=リムーバブルストレージデバイス上の Nepomuk メタデータへのアクセスを提供する Nepomuk サービス
-Comment[kk]=Ауыстырмалы Nepomuk құрылғыда метадеректеріне қатынауды қамтамасыз етіп сақтау қызметі.
-Comment[km]=សេវា​​ឧបករណ៍​ផ្ទុក​ចល័ត​របស់ Nepomuk ដោយ​ផ្ដល់​ការ​ចូលដំណើរការ​ទៅ​កាន់​​ទិន្នន័យ​មេតា​របស់ Nepomuk នៅ​លើ​ឧបករណ៍​ផ្ទុក​ចល័ត ។
-Comment[kn]=ತೆಗೆದು ಹಾಕಬಹುದಾದ ಸಾಧನದಲ್ಲಿರುವ ನೆಪೋಮುಕ್ ಮೆಟಾ ದತ್ತಾಂಶಕ್ಕೆ ನೆಪೋಮುಕ್ ತೆಗೆಧು ಹಾಕಬಹುದಾದ ಶೇಖರಣಾ ಸೇವೆ ಪ್ರವೇಶಾಧಿಕಾರ ನೀಡುತ್ತದೆ. 
-Comment[ko]=Nepomuk 이동식 저장소 서비스는 이동식 저장소에 있는 Nepomuk 메타데이터를 사용합니다.
-Comment[lt]=Nepomuk atjungiamų kaupiklių tarnba, leidžianti pasiekti Nepomuk metaduomenis atjungiamuose kaupikliuose.
-Comment[lv]=Nepomuk noņemamo ierīču serviss, nodrošina piekļuvi pie Nepomuk medatatiem failiem uz noņemamajām ierīcēm.
-Comment[ml]=നീക്കം ചെയ്യാവുന്ന സംഭരണോപകരണങ്ങള്‍ക്കു് നെപ്പോമുക്ക് മെറ്റാഡാറ്റ ഉപയോഗിയ്ക്കാന്‍ അനുവദിയ്ക്കുന്ന നെപ്പോമുക്കിന്റെ നീക്കം ചെയ്യാവുന്ന സംഭരണ സേവനം.
-Comment[nb]=Nepomuk flyttbar lagringstjeneste som gir tilgang til Nepomuk metadata på flyttbare lagringsenheter.
-Comment[nds]=De Nepomuk-Deenst för tuuschbor Loopwarken; stellt Togriep op Nepomuk-Metadaten op tuuschbor Spiekerreedschappen praat.
-Comment[nl]=De Nepomuk verwijderbare opslagservice, levert toegang tot Nepomuk metagegevens over verwijderbare opslagapparaten.
-Comment[nn]=Nepomuk-tenesta for lagringsmedium gjev tilgang til Nepomuk-metadata på flyttbare einingar.
-Comment[pa]=ਨਿਪੋਮੁਕ ਹਟਾਉਣਯੋਗ ਸਟੋਰੇਜ਼ ਸਰਵਿਸ, ਜੋ ਕਿ ਨਿਪੋਮੁਕ ਮੇਟਾਡਾਟਾ ਲਈ ਹਟਾਉਣਯੋਗ ਸਟੋਰੇਜ਼ ਜੰਤਰ ਉੱਤ ਅਸੈੱਸ ਦਿੰਦੀ ਹੈ।
-Comment[pl]=Usługa mediów wymiennych Nepomuka, daje dostęp do danych Nepomuka na wymiennych nośnikach.
-Comment[pt]=O serviço de armazenamento removíveis do Nepomuk, o qual fornece o acesso aos meta-dados do Nepomuk para os dispositivos removíveis de armazenamento.
-Comment[pt_BR]=Serviço de dispositivo de armazenamento removível do Nepomuk, provendo acesso a metadados do Nepomuk nos dispositivos de armazenamento removíveis.
-Comment[ro]=Serviciul Nepomuk de stocare detașabilă, furnizînd acces la metadate Nepomuk aflate pe dispozitive de stocare detașabile.
-Comment[ru]=Служба хранилища Nepomuk на съёмных носителях предоставляет доступ к метаданным Nepomuk на съёмных устройствах хранения.
-Comment[si]=ඉවත්කල හැකි ගබඩා මෙවලම් මත Nepomuk මෙටා දත්ත ප්‍රවේශය සපයමින් Nepomuk ඉවත්කල හැකි ගබඩා සේවාව.
-Comment[sk]=Služba Nepomuku, ktorá poskytuje prístup k metadátam Nepomuku na vymeniteľných úložných zariadeniach.
-Comment[sl]=Storitev odstranljivih nosilcev za semantično namizje, ki omogoča dostop do semantičnih metapodatkov na odstranljivih napravah za shranjevanje.
-Comment[sr]=Непомуков сервис уклоњивих складишта даје приступ Непомуковим метаподацима на уклоњивим складишним уређајима.
-Comment[sr@ijekavian]=Непомуков сервис уклоњивих складишта даје приступ Непомуковим метаподацима на уклоњивим складишним уређајима.
-Comment[sr@ijekavianlatin]=Nepomukov servis uklonjivih skladišta daje pristup Nepomukovim metapodacima na uklonjivim skladišnim uređajima.
-Comment[sr@latin]=Nepomukov servis uklonjivih skladišta daje pristup Nepomukovim metapodacima na uklonjivim skladišnim uređajima.
-Comment[sv]=Nepomuks tjänst för flyttbara medier, vilken ger tillgång till Nepomuk metadata för flyttbara lagringsenheter.
-Comment[th]=บริการเก็บข้อมูล Nepomuk บนสื่อถอด/เสียบได้ ให้บริการในการเข้าใช้งานข้อมูลต่าง ๆ ของ Nepomuk บนอุปกรณ์ต่าง ๆ ที่เป็นประเภทถอด/เสียบได้
-Comment[tr]=Nepomuk çıkarılabilir depolama servisi, çıkarılabilir depolama aygıtlarındaki Nepomuk meta verisine erişmeyi sağlar.
-Comment[uk]=Служба зберігання на портативних носіях Nepomuk надає вам доступ до метаданих Nepomuk, що зберігаються на портативних носіях.
-Comment[x-test]=xxThe Nepomuk removable storage service, providing access to Nepomuk metadata on removable storage devices.xx
-Comment[zh_CN]=Nepomuk 移动存储服务,提供了对移动存储设备上的 Nepomuk 元数据访问功能。
-Comment[zh_TW]=Nepomuk 可移除儲存服務,提供存取可移除裝置的 Nepomuk 描述資料。
diff --git a/nepomuk/services/removablestorage/removablestorageservice.cpp b/nepomuk/services/removablestorage/removablestorageservice.cpp
deleted file mode 100644
index 039ff6f..0000000
--- a/nepomuk/services/removablestorage/removablestorageservice.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-/* This file is part of the KDE Project
-   Copyright (c) 2009-2010 Sebastian Trueg <trueg@kde.org>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public
-   License version 2 as published by the Free Software Foundation.
-
-   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 Library General Public License
-   along with this library; see the file COPYING.LIB.  If not, write to
-   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.
-*/
-
-#include "removablestorageservice.h"
-#include "strigiserviceinterface.h"
-#include "filewatchserviceinterface.h"
-
-#include <QtDBus/QDBusConnection>
-#include <QtCore/QUuid>
-#include <QtCore/QTextStream>
-#include <QtCore/QFile>
-#include <QtCore/QFileInfo>
-
-#include <KDebug>
-#include <KUrl>
-#include <KPluginFactory>
-#include <KStandardDirs>
-#include <KConfig>
-#include <KConfigGroup>
-#include <kdirnotify.h>
-
-#include <Solid/DeviceNotifier>
-#include <Solid/DeviceInterface>
-#include <Solid/Block>
-#include <Solid/Device>
-#include <Solid/StorageDrive>
-#include <Solid/StorageVolume>
-#include <Solid/StorageAccess>
-#include <Solid/Predicate>
-
-#include <Soprano/StatementIterator>
-#include <Soprano/Statement>
-#include <Soprano/NodeIterator>
-#include <Soprano/Node>
-#include <Soprano/Model>
-#include <Soprano/QueryResultIterator>
-#include <Soprano/Serializer>
-#include <Soprano/Parser>
-#include <Soprano/PluginManager>
-#include <Soprano/Util/SimpleStatementIterator>
-#include <Soprano/Vocabulary/RDF>
-#include <Soprano/Vocabulary/NAO>
-#include <Soprano/Vocabulary/NRL>
-#include <Soprano/Vocabulary/Xesam>
-
-#include <Nepomuk/Vocabulary/NFO>
-#include <Nepomuk/Vocabulary/NIE>
-#include <Nepomuk/Resource>
-#include <Nepomuk/Variant>
-
-//
-// A few notes on filex:
-// Relative URLs:
-//   without device: filex:///relative/path
-//   with device:    filex://<UID>/relative/path
-// [Absolute URLs:
-//   without device: filex:///absolute/path
-//   with device:    filex:/<UID>//absolute/path]
-//
-
-using namespace Soprano;
-
-namespace {
-    bool isUsableVolume( const Solid::Device& dev ) {
-        if ( dev.is<Solid::StorageVolume>() &&
-             dev.is<Solid::StorageAccess>() &&
-             dev.parent().is<Solid::StorageDrive>() &&
-             ( dev.parent().as<Solid::StorageDrive>()->isRemovable() ||
-               dev.parent().as<Solid::StorageDrive>()->isHotpluggable() ) ) {
-            const Solid::StorageVolume* volume = dev.as<Solid::StorageVolume>();
-            if ( !volume->isIgnored() && volume->usage() == Solid::StorageVolume::FileSystem )
-                return true;
-        }
-
-        return false;
-    }
-
-    bool isUsableVolume( const QString& udi ) {
-        Solid::Device dev( udi );
-        return isUsableVolume( dev );
-    }
-}
-
-
-Nepomuk::RemovableStorageService::RemovableStorageService( QObject* parent, const QList<QVariant>& )
-    : Service( parent )
-{
-    kDebug();
-
-    initCacheEntries();
-
-    connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceAdded( const QString& ) ),
-             this, SLOT( slotSolidDeviceAdded( const QString& ) ) );
-    connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceRemoved( const QString& ) ),
-             this, SLOT( slotSolidDeviceRemoved( const QString& ) ) );
-}
-
-
-Nepomuk::RemovableStorageService::~RemovableStorageService()
-{
-}
-
-
-QString Nepomuk::RemovableStorageService::resourceUriFromLocalFileUrl( const QString& urlString )
-{
-    KUrl url( urlString );
-    KUrl fileXUrl;
-    QString path = url.path();
-
-    for ( QHash<QString, Entry>::ConstIterator it = m_metadataCache.constBegin();
-          it != m_metadataCache.constEnd(); ++it ) {
-        const Entry& entry = it.value();
-        if ( !entry.m_lastMountPath.isEmpty() && path.startsWith( entry.m_lastMountPath ) ) {
-            // construct the filex:/ URL and use it below
-            fileXUrl = entry.constructRelativeUrl( path );
-            break;
-        }
-    }
-
-
-    //
-    // This is a query similar to the one Resource in libnepomuk uses. Once we found the filex:/ URL above we
-    // can simply continue with that. If the entry does not exist yet libnepomuk will create a new
-    // random nepomuk:/ URL.
-    //
-    QString query;
-    if ( fileXUrl.isEmpty() )
-        query = QString::fromLatin1("select distinct ?r ?o where { "
-                                    "{ ?r %1 %2 . } "
-                                    "UNION "
-                                    "{ %2 ?p ?o . } "
-                                    "} LIMIT 1")
-                .arg( Soprano::Node::resourceToN3(Nepomuk::Vocabulary::NIE::url()),
-                      Soprano::Node::resourceToN3(url) );
-    else
-        query = QString::fromLatin1("select distinct ?r ?o where { "
-                                    "{ ?r %1 %2 . } "
-                                    "UNION "
-                                    "{ ?r %1 %3 . } "
-                                    "UNION "
-                                    "{ %2 ?p ?o . } "
-                                    "} LIMIT 1")
-                .arg( Soprano::Node::resourceToN3(Nepomuk::Vocabulary::NIE::url()),
-                      Soprano::Node::resourceToN3(url),
-                      Soprano::Node::resourceToN3(fileXUrl) );
-
-    Soprano::QueryResultIterator it = mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
-    if( it.next() ) {
-        KUrl resourceUri = it["r"].uri();
-        if( resourceUri.isEmpty() )
-            return url.url();
-        else
-            return resourceUri.url();
-    }
-    else {
-        return QString();
-    }
-}
-
-
-QStringList Nepomuk::RemovableStorageService::currentlyMountedAndIndexed()
-{
-    if( KConfig( "nepomukstrigirc" ).group( "General" ).readEntry( "index newly mounted", false ) ) {
-        QStringList paths;
-        for ( QHash<QString, Entry>::ConstIterator it = m_metadataCache.constBegin();
-              it != m_metadataCache.constEnd(); ++it ) {
-            const Entry& entry = it.value();
-            const Solid::StorageAccess* storage = entry.m_device.as<Solid::StorageAccess>();
-            if ( storage && storage->isAccessible() ) {
-                paths << storage->filePath();
-            }
-        }
-        return paths;
-    }
-    else {
-        return QStringList();
-    }
-}
-
-
-void Nepomuk::RemovableStorageService::initCacheEntries()
-{
-    QList<Solid::Device> devices
-        = Solid::Device::listFromQuery( QLatin1String( "StorageVolume.usage=='FileSystem'" ) );
-    foreach( const Solid::Device& dev, devices ) {
-        if ( isUsableVolume( dev ) ) {
-            Entry* entry = createCacheEntry( dev );
-            const Solid::StorageAccess* storage = entry->m_device.as<Solid::StorageAccess>();
-            if ( storage && storage->isAccessible() )
-                slotAccessibilityChanged( true, dev.udi() );
-        }
-    }
-}
-
-
-Nepomuk::RemovableStorageService::Entry* Nepomuk::RemovableStorageService::createCacheEntry( const Solid::Device& dev )
-{
-    Entry entry( this );
-    entry.m_device = dev;
-    entry.m_description = dev.description();
-    entry.m_uuid = entry.m_device.as<Solid::StorageVolume>()->uuid();
-    connect( dev.as<Solid::StorageAccess>(), SIGNAL(accessibilityChanged(bool, QString)),
-             this, SLOT(slotAccessibilityChanged(bool, QString)) );
-
-    m_metadataCache.insert( dev.udi(), entry );
-
-    kDebug() << "Found removable storage volume for Nepomuk docking:" << dev.udi() << dev.description();
-
-    return &m_metadataCache[dev.udi()];
-}
-
-
-Nepomuk::RemovableStorageService::Entry* Nepomuk::RemovableStorageService::findEntryByFilePath( const QString& path )
-{
-    for( QHash<QString, Entry>::iterator it = m_metadataCache.begin();
-         it != m_metadataCache.end(); ++it ) {
-        Entry& entry = *it;
-        if ( entry.m_device.as<Solid::StorageAccess>()->isAccessible() &&
-             path.startsWith( entry.m_device.as<Solid::StorageAccess>()->filePath() ) )
-            return &entry;
-    }
-    return 0;
-}
-
-
-void Nepomuk::RemovableStorageService::slotSolidDeviceAdded( const QString& udi )
-{
-    kDebug() << udi;
-
-    if ( isUsableVolume( udi ) ) {
-        createCacheEntry( Solid::Device( udi ) );
-    }
-}
-
-
-void Nepomuk::RemovableStorageService::slotSolidDeviceRemoved( const QString& udi )
-{
-    kDebug() << udi;
-    if ( m_metadataCache.contains( udi ) ) {
-        kDebug() << "Found removable storage volume for Nepomuk undocking:" << udi;
-        m_metadataCache.remove( udi );
-    }
-}
-
-
-void Nepomuk::RemovableStorageService::slotAccessibilityChanged( bool accessible, const QString& udi )
-{
-    kDebug() << accessible << udi;
-
-    Entry& entry = m_metadataCache[udi];
-    if ( accessible ) {
-        //
-        // cache new mount path
-        //
-        entry.m_lastMountPath = entry.m_device.as<Solid::StorageAccess>()->filePath();
-
-        if ( entry.hasLastMountPath() ) {
-            //
-            // tell the filewatch service that it should monitor the new medium
-            //
-            org::kde::nepomuk::FileWatch( "org.kde.nepomuk.services.nepomukfilewatch",
-                                          "/nepomukfilewatch",
-                                          QDBusConnection::sessionBus() )
-                .watchFolder( entry.m_lastMountPath );
-
-
-            //
-            // tell Strigi to update the newly mounted device
-            //
-            if( KConfig( "nepomukstrigirc" ).group( "General" ).readEntry( "index newly mounted", false ) ) {
-                org::kde::nepomuk::Strigi( "org.kde.nepomuk.services.nepomukstrigiservice",
-                                           "/nepomukstrigiservice",
-                                           QDBusConnection::sessionBus() )
-                    .indexFolder( entry.m_lastMountPath, true /* recursive */, false /* no force */ );
-            }
-        }
-
-        //
-        // Remove all metadata for files that have been removed from the media whilst it was not mounted with us.
-        // The Strigi service cannot handle this since it does not support filex:/ URLs when updating folders.
-        //
-        QString query = QString::fromLatin1( "select ?url ?r where { "
-                                             "?r a %1 . "
-                                             "?r %2 ?fs . "
-                                             "?r %3 ?url . "
-                                             "?fs a %4 . "
-                                             "?fs %5 %6 . "
-                                             "}" )
-                        .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NFO::FileDataObject() ),
-                              Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::isPartOf() ),
-                              Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                              Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NFO::Filesystem() ),
-                              Soprano::Node::resourceToN3( Soprano::Vocabulary::NAO::identifier() ),
-                              Soprano::Node::literalToN3( entry.m_uuid ) );
-        Soprano::QueryResultIterator it = mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
-        while ( it.next() ) {
-            QString path = entry.constructLocalPath( it["url"].uri() );
-            if ( !QFile::exists( path ) ) {
-                kDebug() << "Removing metadata for no longer existing" << path;
-                Nepomuk::Resource( it["r"].uri() ).remove();
-            }
-        }
-    }
-    else if ( entry.hasLastMountPath() ) {
-        //
-        // The first thing we need to do is to inform nepomuk:/ kio slave instances that something has changed
-        // so any caches will be cleared. Otherwise KDirModel and friends might try to access the old media URLs
-        // which nepomuk:/ KIO has rewritten without asking again.
-        //
-        // FIXME: cannot use "org::kde::KDirNotify::emitFilesRemoved( QStringList() << entry.m_lastMountPath );"
-        //        as that will make the FileWatchService delete all metadata
-        //
-
-        //
-        // First we create the filesystem resource. We mostly need this for the user readable label.
-        //
-        Nepomuk::Resource fsRes( entry.m_uuid, Nepomuk::Vocabulary::NFO::Filesystem() );
-        fsRes.setLabel( entry.m_description );
-
-        //
-        // We change all absolute file:/ URLs to relative filex:/ URLs which include the solid id
-        //
-        // FIXME: do this asyncroneously
-        //
-        QString query = QString::fromLatin1( "select ?r ?url ?g where { " // FIXME: can Virtuoso directly select a substring of the url? Can we maybe even do this in an update query?
-                                             "graph ?g { ?r %1 ?url . } . "
-                                             "FILTER(REGEX(STR(?url),'^file://%2/')) . }" )
-                        .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ) )
-                        .arg( entry.m_lastMountPath );
-        kDebug() << query;
-        QList<Soprano::BindingSet> bindings = mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql ).allBindings();
-
-        foreach( const Soprano::BindingSet& b, bindings ) {
-            const QUrl resource = b["r"].uri();
-            const QString path = b["url"].uri().path();
-            const QUrl graph = b["g"].uri();
-
-            // construct the new filex:/ URL from the solid device UID and the relative path on the device
-            const QUrl filexUrl = entry.constructRelativeUrl( path );
-
-            kDebug() << "Converting URL" << b["url"] << "to" << filexUrl;
-
-            // for performance reasons we do not use Nepomuk::Resource but do it the old fashioned way
-            mainModel()->removeAllStatements( resource, Nepomuk::Vocabulary::NIE::url(), Node() );
-            mainModel()->addStatement( resource, Nepomuk::Vocabulary::NIE::url(), filexUrl, graph );
-
-            // IDEA: What about nie:hasPart nfo:FileSystem for all top-level items instead of the nfo:Folder that is the mount point.
-            // But then we run into the recursion problem
-            Resource fileRes( resource );
-            fileRes.addProperty( Nepomuk::Vocabulary::NIE::isPartOf(), fsRes );
-        }
-
-        //
-        // the mount point is no longer the parent folder of the top level files on the removable device
-        // We need to delete those relations seperately
-        //
-        QUrl parentUrl = Resource( KUrl( entry.m_lastMountPath ) ).resourceUri();
-        if( parentUrl.isValid() )
-            mainModel()->removeAllStatements( Soprano::Node(), Nepomuk::Vocabulary::NIE::isPartOf(), parentUrl );
-    }
-    kDebug() << "done";
-}
-
-
-Nepomuk::RemovableStorageService::Entry::Entry( RemovableStorageService* parent )
-    : q( parent )
-{
-}
-
-
-KUrl Nepomuk::RemovableStorageService::Entry::constructRelativeUrl( const QString& path ) const
-{
-    const QString relativePath = path.mid( m_lastMountPath.count() );
-    return KUrl( QLatin1String("filex://") + m_uuid + relativePath );
-}
-
-
-QString Nepomuk::RemovableStorageService::Entry::constructLocalPath( const KUrl& filexUrl ) const
-{
-    QString path( m_lastMountPath );
-    if ( path.endsWith( QLatin1String( "/" ) ) )
-        path.truncate( path.length()-1 );
-    path += filexUrl.path();
-    return path;
-}
-
-
-bool Nepomuk::RemovableStorageService::Entry::hasLastMountPath() const
-{
-    return( !m_lastMountPath.isEmpty() &&
-            m_lastMountPath != QLatin1String( "/" ) );
-}
-
-NEPOMUK_EXPORT_SERVICE( Nepomuk::RemovableStorageService, "nepomukremovablestorageservice")
-
-#include "removablestorageservice.moc"
diff --git a/nepomuk/services/removablestorage/removablestorageservice.h b/nepomuk/services/removablestorage/removablestorageservice.h
deleted file mode 100644
index 687acce..0000000
--- a/nepomuk/services/removablestorage/removablestorageservice.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/* This file is part of the KDE Project
-   Copyright (c) 2009 Sebastian Trueg <trueg@kde.org>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public
-   License version 2 as published by the Free Software Foundation.
-
-   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 Library General Public License
-   along with this library; see the file COPYING.LIB.  If not, write to
-   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.
-*/
-
-#ifndef _NEPOMUK_REMOVABLE_STORAGE_SERVICE_H_
-#define _NEPOMUK_REMOVABLE_STORAGE_SERVICE_H_
-
-#include <Nepomuk/Service>
-
-#include <QtCore/QUrl>
-#include <QtCore/QVariant>
-#include <QtCore/QHash>
-
-#include <Solid/Device>
-
-namespace Solid {
-    class StorageAccess;
-    class StorageVolume;
-}
-
-class KUrl;
-
-namespace Nepomuk {
-    /**
-     * This service caches metadata stored on removable devices such as usb sticks in the local
-     * Nepomuk repository. Thus, the metadata can be searched like any other local file metadata.
-     *
-     * Once a device is mounted, the data is cached. When unmounted the cached data is removed again.
-     */
-    class RemovableStorageService : public Service
-    {
-        Q_OBJECT
-        Q_CLASSINFO( "D-Bus Interface", "org.kde.nepomuk.RemovableStorage" )
-
-    public:
-        RemovableStorageService( QObject* parent, const QVariantList& );
-        ~RemovableStorageService();
-
-    public Q_SLOTS:
-        /**
-         * Determines the resource URI for a local file URL. This also handles
-         * local file URLs that are not stored as such in Nepomuk but using a
-         * filex:/ scheme URL for a mounted filesystem.
-         *
-         * This method is called by libnepomuk's Resource class to handle
-         * filex:/ URLs transparently.
-         *
-         * \return The resource URI for \p url or an empty string if it could
-         * not be found (this is the case if the filesystem is not mounted or
-         * if \p url is simply invalid or not a used URL.)
-         */
-        Q_SCRIPTABLE QString resourceUriFromLocalFileUrl( const QString& url );
-
-        Q_SCRIPTABLE QStringList currentlyMountedAndIndexed();
-
-    private Q_SLOTS:
-        void slotSolidDeviceAdded( const QString& udi );
-        void slotSolidDeviceRemoved( const QString& udi );
-
-        /**
-         * Reacts on Solid::StorageAccess::accessibilityChanged:
-         * \li If \p accessible is true, the metadata from the removable storage
-         * is cached in the local Nepomuk store.
-         * \li Otherwise the corresponding cache is removed.
-         */
-        void slotAccessibilityChanged( bool accessible, const QString& udi );
-
-    private:
-        void initCacheEntries();
-
-        class Entry {
-            RemovableStorageService* q;
-
-        public:
-            Entry() {}
-            Entry( RemovableStorageService* );
-
-            KUrl constructRelativeUrl( const QString& path ) const;
-            QString constructLocalPath( const KUrl& filexUrl ) const;
-            bool hasLastMountPath() const;
-
-            Solid::Device m_device;
-            QString m_lastMountPath;
-
-            // need to cache the device properties in case
-            // an USB device is simply ejected without properly
-            // unmounting before
-            QString m_description;
-            QString m_uuid;
-        };
-        QHash<QString, Entry> m_metadataCache;
-
-        Entry* createCacheEntry( const Solid::Device& dev );
-        Entry* findEntryByFilePath( const QString& path );
-    };
-}
-
-#endif // _NEPOMUK_REMOVABLE_STORAGE_SERVICE_H_
diff --git a/nepomuk/services/storage/CMakeLists.txt b/nepomuk/services/storage/CMakeLists.txt
index ee0781d..f882d34 100644
--- a/nepomuk/services/storage/CMakeLists.txt
+++ b/nepomuk/services/storage/CMakeLists.txt
@@ -6,6 +6,8 @@ include_directories(
   ${SOPRANO_INCLUDE_DIR}
   ${CMAKE_SOURCE_DIR}
   ${NEPOMUK_INCLUDE_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/lib
+  ${CMAKE_CURRENT_SOURCE_DIR}/../backupsync/lib
   )
 
 set(storage_SRCS
@@ -18,20 +20,28 @@ set(storage_SRCS
   graphretriever.cpp
   crappyinferencer.cpp
   crappyinferencer2.cpp
-  typevisibilitytree.cpp
+  removablemediamodel.cpp
+  datamanagementmodel.cpp
+  datamanagementadaptor.cpp
+  datamanagementcommand.cpp
+  classandpropertytree.cpp
+  resourcemerger.cpp
+  resourceidentifier.cpp
+  resourcewatchermanager.cpp
+  resourcewatcherconnection.cpp
+  graphmaintainer.cpp
   )
 
-soprano_add_ontology(storage_SRCS
-  ${CMAKE_CURRENT_SOURCE_DIR}/../../ontologies/kuvo.trig
-  "KUVO"
-  "Nepomuk::Vocabulary"
-  "trig")
-
 qt4_add_dbus_adaptor(storage_SRCS
   ../../interfaces/org.kde.nepomuk.OntologyManager.xml
   ontologyloader.h
   Nepomuk::OntologyLoader)
 
+qt4_add_dbus_adaptor(storage_SRCS
+  ../../interfaces/org.kde.nepomuk.ResourceWatcherConnection.xml
+  resourcewatcherconnection.h
+  Nepomuk::ResourceWatcherConnection)
+
 set(HAVE_SOPRANO_INDEX ${SopranoIndex_FOUND})
 
 kde4_add_plugin(nepomukstorage ${storage_SRCS})
@@ -41,7 +51,11 @@ target_link_libraries(nepomukstorage
   ${SOPRANO_SERVER_LIBRARIES}
   ${KDE4_KDECORE_LIBS}
   ${KDE4_KIO_LIBS}
+  ${KDE4_SOLID_LIBS}
   ${NEPOMUK_LIBRARIES}
+  nepomuksync
+  nepomukdatamanagement
+  nepomukcommon
   )
 
 install(
@@ -57,4 +71,5 @@ install(
   DESTINATION ${PLUGIN_INSTALL_DIR})
 # -----------------------------
 
+add_subdirectory(lib)
 add_subdirectory(test)
diff --git a/nepomuk/services/storage/Messages.sh b/nepomuk/services/storage/Messages.sh
index 494d8c5..1574b8e 100755
--- a/nepomuk/services/storage/Messages.sh
+++ b/nepomuk/services/storage/Messages.sh
@@ -1,2 +1,2 @@
 #! /usr/bin/env bash
-$XGETTEXT `find . -name "*.cpp"` -o $podir/nepomukstorage.pot
+$XGETTEXT `find . -name "*.cpp" | grep -v '/test/'` -o $podir/nepomukstorage.pot
diff --git a/nepomuk/services/storage/classandpropertytree.cpp b/nepomuk/services/storage/classandpropertytree.cpp
new file mode 100644
index 0000000..cb16f07
--- /dev/null
+++ b/nepomuk/services/storage/classandpropertytree.cpp
@@ -0,0 +1,680 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "classandpropertytree.h"
+#include "simpleresource.h"
+#include "simpleresourcegraph.h"
+
+#include <QtCore/QSet>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QDateTime>
+#include <QtCore/QMutexLocker>
+
+#include <Soprano/Version>
+#include <Soprano/Node>
+#include <Soprano/LiteralValue>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/Model>
+#include <Soprano/Vocabulary/RDFS>
+#include <Soprano/Vocabulary/NAO>
+#include <Soprano/Vocabulary/NRL>
+#include <Soprano/Vocabulary/XMLSchema>
+
+#include <KDebug>
+
+using namespace Soprano;
+using namespace Soprano::Vocabulary;
+
+Nepomuk::ClassAndPropertyTree* Nepomuk::ClassAndPropertyTree::s_self = 0;
+
+class Nepomuk::ClassAndPropertyTree::ClassOrProperty
+{
+public:
+    ClassOrProperty()
+        : isProperty(false),
+          maxCardinality(0),
+          userVisible(0),
+          identifying(0) {
+    }
+
+    /// true if this is a property, for classes this is false
+    bool isProperty;
+
+    /// the uri of the class or property
+    QUrl uri;
+
+    /// the direct parents, ie. those for which a rdfs relations exists
+    QSet<QUrl> directParents;
+
+    /// includes all parents, even grand-parents and further up
+    QSet<QUrl> allParents;
+
+    /// the max cardinality if this is a property with a max cardinality set, 0 otherwise
+    int maxCardinality;
+
+    /// 0 - undecided, 1 - visible, -1 - non-visible
+    int userVisible;
+
+    /// 0 - undecided, 1 - identifying, -1 - non-identifying
+    int identifying;
+
+    /// only valid for properties
+    QUrl domain;
+    QUrl range;
+};
+
+Nepomuk::ClassAndPropertyTree::ClassAndPropertyTree(QObject *parent)
+    : QObject(parent),
+      m_mutex(QMutex::Recursive)
+{
+    Q_ASSERT(s_self == 0);
+    s_self = this;
+}
+
+Nepomuk::ClassAndPropertyTree::~ClassAndPropertyTree()
+{
+    qDeleteAll(m_tree);
+    s_self = 0;
+}
+
+bool Nepomuk::ClassAndPropertyTree::isKnownClass(const QUrl &uri) const
+{
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(uri))
+        return !cop->isProperty;
+    else
+        return false;
+}
+
+QSet<QUrl> Nepomuk::ClassAndPropertyTree::allParents(const QUrl &uri) const
+{
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(uri))
+        return cop->allParents;
+    else
+        return QSet<QUrl>();
+}
+
+bool Nepomuk::ClassAndPropertyTree::isChildOf(const QUrl &type, const QUrl &superClass) const
+{
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(type))
+        return cop->allParents.contains(superClass);
+    else
+        return 0;
+}
+
+bool Nepomuk::ClassAndPropertyTree::isChildOf(const QList< QUrl >& types, const QUrl& superClass) const
+{
+    if(superClass == RDFS::Resource()) {
+        return true;
+    }
+
+    foreach( const QUrl & type, types ) {
+        if( isChildOf( type, superClass ) )
+            return true;
+    }
+    return false;
+}
+
+int Nepomuk::ClassAndPropertyTree::maxCardinality(const QUrl &type) const
+{
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(type))
+        return cop->maxCardinality;
+    else
+        return 0;
+}
+
+bool Nepomuk::ClassAndPropertyTree::isUserVisible(const QUrl &type) const
+{
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(type))
+        return cop->userVisible == 1;
+    else
+        return true;
+}
+
+QUrl Nepomuk::ClassAndPropertyTree::propertyDomain(const QUrl &uri) const
+{
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(uri))
+        return cop->domain;
+    else
+        return QUrl();
+}
+
+QUrl Nepomuk::ClassAndPropertyTree::propertyRange(const QUrl &uri) const
+{
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(uri))
+        return cop->range;
+    else
+        return QUrl();
+}
+
+bool Nepomuk::ClassAndPropertyTree::hasLiteralRange(const QUrl &uri) const
+{
+    // TODO: this is a rather crappy check for literal range
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(uri))
+        return (cop->range.toString().startsWith(XMLSchema::xsdNamespace().toString() ) ||
+                cop->range == RDFS::Literal());
+    else
+        return false;
+}
+
+bool Nepomuk::ClassAndPropertyTree::isIdentifyingProperty(const QUrl &uri) const
+{
+    QMutexLocker lock(&m_mutex);
+    if(const ClassOrProperty* cop = findClassOrProperty(uri))
+        return cop->identifying == 1;
+    else
+        return true; // we default to true for unknown properties to ensure that we never perform invalid merges
+}
+
+Soprano::Node Nepomuk::ClassAndPropertyTree::variantToNode(const QVariant &value, const QUrl &property) const
+{
+    QSet<Soprano::Node> nodes = variantListToNodeSet(QVariantList() << value, property);
+    if(nodes.isEmpty())
+        return Soprano::Node();
+    else
+        return *nodes.begin();
+}
+
+
+namespace Soprano {
+namespace Vocabulary {
+    namespace XMLSchema {
+        QUrl xsdDuration() {
+            return QUrl( Soprano::Vocabulary::XMLSchema::xsdNamespace().toString() + QLatin1String("duration") );
+        }
+    }
+}
+}
+
+QSet<Soprano::Node> Nepomuk::ClassAndPropertyTree::variantListToNodeSet(const QVariantList &vl, const QUrl &property) const
+{
+    clearError();
+
+    QSet<Soprano::Node> nodes;
+    QUrl range = propertyRange(property);
+
+    //
+    // Special case: xsd:duration - Soprano doesn't handle it
+    //
+    if(range == XMLSchema::xsdDuration()) {
+        range = XMLSchema::unsignedInt();
+    }
+
+    //
+    // Special case: rdfs:Literal
+    //
+    if(range == RDFS::Literal()) {
+        Q_FOREACH(const QVariant& value, vl) {
+            nodes.insert(Soprano::LiteralValue::createPlainLiteral(value.toString()));
+        }
+    }
+
+    //
+    // Special case: abstract properties - we do not allow setting them
+    //
+    else if(range.isEmpty()) {
+        setError(QString::fromLatin1("Cannot set values for abstract property '%1'.").arg(property.toString()), Soprano::Error::ErrorInvalidArgument);
+        return QSet<Soprano::Node>();
+    }
+
+    //
+    // The standard case
+    //
+    else {
+        const QVariant::Type literalType = Soprano::LiteralValue::typeFromDataTypeUri(range);
+        if(literalType == QVariant::Invalid) {
+            Q_FOREACH(const QVariant& value, vl) {
+                // treat as a resource range for now
+                if(value.type() == QVariant::Url) {
+                    nodes.insert(value.toUrl());
+                }
+                else if(value.type() == QVariant::String) {
+                    QString s = value.toString();
+                    if(!s.isEmpty()) {
+                        // for convinience we support local file paths
+                        if(s[0] == QDir::separator() && QFile::exists(s)) {
+                            nodes.insert(QUrl::fromLocalFile(s));
+                        }
+                        else {
+                            // treat it as a URI
+                            nodes.insert(QUrl(s));
+                        }
+                    }
+                    else {
+                        // empty string
+                        setError(QString::fromLatin1("Encountered an empty string where a resource URI was expected."), Soprano::Error::ErrorInvalidArgument);
+                        return QSet<Soprano::Node>();
+                    }
+                }
+                else {
+                    // invalid type
+                    setError(QString::fromLatin1("Encountered '%1' where a resource URI was expected.").arg(value.toString()), Soprano::Error::ErrorInvalidArgument);
+                    return QSet<Soprano::Node>();
+                }
+            }
+        }
+        else {
+            Q_FOREACH(const QVariant& value, vl) {
+                //
+                // Exiv data often contains floating point values encoded as a fraction
+                //
+                if((range == XMLSchema::xsdFloat() || range == XMLSchema::xsdDouble())
+                        && value.type() == QVariant::String) {
+                    int x = 0;
+                    int y = 0;
+                    if ( sscanf( value.toString().toLatin1().data(), "%d/%d", &x, &y ) == 2 && y != 0 ) {
+                        const double v = double( x )/double( y );
+#if SOPRANO_IS_VERSION(2, 6, 51)
+                        nodes.insert(LiteralValue::fromVariant(v, range));
+#else
+                        nodes.insert(LiteralValue::fromString(QString::number(v), range));
+#endif
+                        continue;
+                    }
+                }
+
+                //
+                // ID3 tags sometimes only contain the year of publication. We cover this
+                // special case here with a very dumb heuristic
+                //
+                else if(range == XMLSchema::dateTime()
+                        && value.canConvert(QVariant::UInt)) {
+                    bool ok = false;
+                    const int t = value.toInt(&ok);
+                    if(ok && t > 0 && t <= 9999) {
+                        nodes.insert(LiteralValue(QDateTime(QDate(t, 1, 1), QTime(0, 0), Qt::UTC)));
+                        continue;
+                    }
+                }
+
+#if SOPRANO_IS_VERSION(2, 6, 51)
+                Soprano::LiteralValue v = Soprano::LiteralValue::fromVariant(value, range);
+                if(v.isValid()) {
+                    nodes.insert(v);
+                }
+                else {
+                    // failed literal conversion
+                    setError(QString::fromLatin1("Failed to convert '%1' to literal of type '%2'.").arg(value.toString(), range.toString()), Soprano::Error::ErrorInvalidArgument);
+                    return QSet<Soprano::Node>();
+                }
+#else
+                //
+                // We handle a few special cases here.
+                //
+                // Special Case 1: support conversion from time_t to QDateTime
+                //
+                if(range == XMLSchema::dateTime() &&
+                        value.canConvert(QVariant::UInt)) {
+                    bool ok = false;
+                    int v = value.toUInt(&ok);
+                    if(ok) {
+                        nodes.insert(Soprano::LiteralValue(QDateTime::fromTime_t(v)));
+                        continue;
+                    }
+                }
+
+                //
+                // if the types differ we try to convert
+                // (We need to treat int and friends as a special case since xsd defines more than one
+                // type mapping to them.)
+                //
+                if(value.type() != literalType
+                        || ((value.type() == QVariant::Int ||
+                             value.type() == QVariant::UInt ||
+                             value.type() == QVariant::Double)
+                            && range != Soprano::LiteralValue::dataTypeUriFromType(value.type()))) {
+                    Soprano::LiteralValue v = Soprano::LiteralValue::fromString(value.toString(), range);
+                    if(v.isValid()) {
+                        nodes.insert(v);
+                    }
+                    else {
+                        // failed literal conversion
+                        setError(QString::fromLatin1("Failed to convert '%1' to literal of type '%2'.").arg(value.toString(), range.toString()), Soprano::Error::ErrorInvalidArgument);
+                        return QSet<Soprano::Node>();
+                    }
+                }
+                else {
+                    // we already have the correct type
+                    nodes.insert(Soprano::LiteralValue(value));
+                }
+#endif
+            }
+        }
+    }
+
+    return nodes;
+}
+
+void Nepomuk::ClassAndPropertyTree::rebuildTree(Soprano::Model* model)
+{
+    QMutexLocker lock(&m_mutex);
+
+    qDeleteAll(m_tree);
+    m_tree.clear();
+
+    QString query
+            = QString::fromLatin1("select distinct ?r ?p ?v ?mc ?c ?domain ?range ?ct ?pt "
+                                  "where { "
+                                  "{ ?r a ?ct . FILTER(?ct=rdfs:Class) . "
+                                  "OPTIONAL { ?r rdfs:subClassOf ?p . ?p a rdfs:Class . } . } "
+                                  "UNION "
+                                  "{ ?r a ?pt . FILTER(?pt=rdf:Property) . "
+                                  "OPTIONAL { ?r rdfs:subPropertyOf ?p . ?p a rdf:Property . } . } "
+                                  "OPTIONAL { ?r %1 ?mc . } . "
+                                  "OPTIONAL { ?r %2 ?c . } . "
+                                  "OPTIONAL { ?r %3 ?v . } . "
+                                  "OPTIONAL { ?r %4 ?domain . } . "
+                                  "OPTIONAL { ?r %5 ?range . } . "
+                                  "FILTER(?r!=%6) . "
+                                  "}" )
+            .arg(Soprano::Node::resourceToN3(NRL::maxCardinality()),
+                 Soprano::Node::resourceToN3(NRL::cardinality()),
+                 Soprano::Node::resourceToN3(NAO::userVisible()),
+                 Soprano::Node::resourceToN3(RDFS::domain()),
+                 Soprano::Node::resourceToN3(RDFS::range()),
+                 Soprano::Node::resourceToN3(RDFS::Resource()));
+//    kDebug() << query;
+    Soprano::QueryResultIterator it
+            = model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
+    while( it.next() ) {
+        const QUrl r = it["r"].uri();
+        const Soprano::Node p = it["p"];
+        const Soprano::Node v = it["v"];
+        int mc = it["mc"].literal().toInt();
+        int c = it["c"].literal().toInt();
+        const QUrl domain = it["domain"].uri();
+        const QUrl range = it["range"].uri();
+
+        ClassOrProperty* r_cop = 0;
+        QHash<QUrl, ClassOrProperty*>::iterator copIt = m_tree.find(r);
+        if(copIt != m_tree.end()) {
+            r_cop = copIt.value();
+        }
+        else {
+            r_cop = new ClassOrProperty;
+            r_cop->uri = r;
+            m_tree.insert( r, r_cop );
+        }
+
+        r_cop->isProperty = it["pt"].isValid();
+
+        if( v.isLiteral() ) {
+            r_cop->userVisible = (v.literal().toBool() ? 1 : -1);
+        }
+
+        if(mc > 0 || c > 0) {
+            r_cop->maxCardinality = qMax(mc, c);
+        }
+
+        if(!domain.isEmpty()) {
+            r_cop->domain = domain;
+        }
+
+        if(!range.isEmpty()) {
+            r_cop->range = range;
+        }
+        else {
+            // no range -> resource range
+            r_cop->identifying = -1;
+        }
+
+        if ( p.isResource() &&
+                p.uri() != r &&
+                p.uri() != RDFS::Resource() ) {
+            ClassOrProperty* p_cop = 0;
+            if ( !m_tree.contains( p.uri() ) ) {
+                p_cop = new ClassOrProperty;
+                p_cop->uri = p.uri();
+                m_tree.insert( p.uri(), p_cop );
+            }
+            r_cop->directParents.insert(p.uri());
+        }
+    }
+
+    // although nao:identifier is actually an abstract property Nepomuk has been using
+    // it for very long to store string identifiers (instead of nao:personalIdentifier).
+    // Thus, we force its range to xsd:string for correct conversion in variantListToNodeSet()
+    if(m_tree.contains(NAO::identifier()))
+        m_tree[NAO::identifier()]->range = XMLSchema::string();
+
+    // make sure rdfs:Resource is visible by default
+    ClassOrProperty* rdfsResourceNode = 0;
+    QHash<QUrl, ClassOrProperty*>::iterator rdfsResourceIt = m_tree.find(RDFS::Resource());
+    if( rdfsResourceIt == m_tree.end() ) {
+        rdfsResourceNode = new ClassOrProperty;
+        rdfsResourceNode->uri = RDFS::Resource();
+        m_tree.insert( RDFS::Resource(), rdfsResourceNode );
+    }
+    else {
+        rdfsResourceNode = rdfsResourceIt.value();
+    }
+    if( rdfsResourceNode->userVisible == 0 ) {
+        rdfsResourceNode->userVisible = 1;
+    }
+    // add rdfs:Resource as parent for all top-level classes
+    for ( QHash<QUrl, ClassOrProperty*>::iterator it = m_tree.begin();
+          it != m_tree.end(); ++it ) {
+        if( it.value() != rdfsResourceNode && it.value()->directParents.isEmpty() ) {
+            it.value()->directParents.insert( RDFS::Resource() );
+        }
+    }
+
+    // update all visibility
+    for ( QHash<QUrl, ClassOrProperty*>::iterator it = m_tree.begin();
+          it != m_tree.end(); ++it ) {
+        QSet<QUrl> visitedNodes;
+        updateUserVisibility( it.value(), visitedNodes );
+    }
+
+    // complete the allParents lists
+    for ( QHash<QUrl, ClassOrProperty*>::iterator it = m_tree.begin();
+          it != m_tree.end(); ++it ) {
+        QSet<QUrl> visitedNodes;
+        getAllParents( it.value(), visitedNodes );
+    }
+
+    // update all identifying and flux properties
+    // by default all properties with a literal range are identifying
+    // and all properties with a resource range are non-idenifying
+    query = QString::fromLatin1("select ?p ?t where { "
+                                "?p a rdf:Property . "
+                                "?p a ?t . FILTER(?t!=rdf:Property) . }");
+    it = model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
+    while( it.next() ) {
+        const QUrl p = it["p"].uri();
+        const QUrl t = it["t"].uri();
+
+        if(t == QUrl(NRL::nrlNamespace().toString() + QLatin1String("IdentifyingProperty"))) {
+            m_tree[p]->identifying = 1;
+        }
+        else if(t == QUrl(NRL::nrlNamespace().toString() + QLatin1String("FluxProperty"))) {
+            m_tree[p]->identifying = -1;
+        }
+    }
+    for ( QHash<QUrl, ClassOrProperty*>::iterator it = m_tree.begin();
+          it != m_tree.end(); ++it ) {
+        if(it.value()->isProperty) {
+            QSet<QUrl> visitedNodes;
+            updateIdentifying( it.value(), visitedNodes );
+        }
+    }
+}
+
+const Nepomuk::ClassAndPropertyTree::ClassOrProperty * Nepomuk::ClassAndPropertyTree::findClassOrProperty(const QUrl &uri) const
+{
+    QHash<QUrl, ClassOrProperty*>::const_iterator it = m_tree.constFind(uri);
+    if(it == m_tree.constEnd())
+        return 0;
+    else
+        return it.value();
+}
+
+bool Nepomuk::ClassAndPropertyTree::contains(const QUrl& uri) const
+{
+    return m_tree.contains(uri);
+}
+
+
+/**
+ * Set the value of nao:userVisible.
+ * A class is visible if it has at least one visible direct parent class.
+ */
+int Nepomuk::ClassAndPropertyTree::updateUserVisibility( ClassOrProperty* cop, QSet<QUrl>& visitedNodes )
+{
+    if ( cop->userVisible != 0 ) {
+        return cop->userVisible;
+    }
+    else {
+        for ( QSet<QUrl>::iterator it = cop->directParents.begin();
+             it != cop->directParents.end(); ++it ) {
+            // avoid endless loops
+            if( visitedNodes.contains(*it) )
+                continue;
+            visitedNodes.insert(*it);
+            if ( updateUserVisibility( m_tree[*it], visitedNodes ) == 1 ) {
+                cop->userVisible = 1;
+                break;
+            }
+        }
+        if ( cop->userVisible == 0 ) {
+            // default to invisible
+            cop->userVisible = -1;
+        }
+        //kDebug() << "Setting nao:userVisible of" << cop->uri.toString() << ( cop->userVisible == 1 );
+        return cop->userVisible;
+    }
+}
+
+/**
+ * Set the value of identifying.
+ * An identifying property has at least one identifying direct parent property.
+ */
+int Nepomuk::ClassAndPropertyTree::updateIdentifying( ClassOrProperty* cop, QSet<QUrl>& identifyingNodes )
+{
+    if ( cop->identifying != 0 ) {
+        return cop->identifying;
+    }
+    else {
+        for ( QSet<QUrl>::iterator it = cop->directParents.begin();
+             it != cop->directParents.end(); ++it ) {
+            // avoid endless loops
+            if( identifyingNodes.contains(*it) )
+                continue;
+            identifyingNodes.insert(*it);
+            if ( updateIdentifying( m_tree[*it], identifyingNodes ) == 1 ) {
+                cop->identifying = 1;
+                break;
+            }
+        }
+        if ( cop->identifying == 0 ) {
+            // properties with a literal range default to identifying
+            cop->identifying = hasLiteralRange(cop->uri) ? 1 : -1;
+        }
+        //kDebug() << "Setting identifying of" << cop->uri.toString() << ( cop->identifying == 1 );
+        return cop->identifying;
+    }
+}
+
+QSet<QUrl> Nepomuk::ClassAndPropertyTree::getAllParents(ClassOrProperty* cop, QSet<QUrl>& visitedNodes)
+{
+    if(cop->allParents.isEmpty()) {
+        for ( QSet<QUrl>::iterator it = cop->directParents.begin();
+             it != cop->directParents.end(); ++it ) {
+            // avoid endless loops
+            if( visitedNodes.contains(*it) )
+                continue;
+            visitedNodes.insert( *it );
+            cop->allParents += getAllParents(m_tree[*it], visitedNodes);
+        }
+        cop->allParents += cop->directParents;
+
+        // some cleanup to fix inheritance loops
+        cop->allParents << RDFS::Resource();
+        cop->allParents.remove(cop->uri);
+    }
+    return cop->allParents;
+}
+
+
+namespace {
+    Soprano::Node convertIfBlankNode( const Soprano::Node & n ) {
+        if( n.isResource() ) {
+            const QString uriString = n.uri().toString();
+            if( uriString.startsWith("_:") ) {
+                return Soprano::Node( uriString.mid(2) ); // "_:" take 2 characters
+            }
+        }
+        return n;
+    }
+}
+
+QList<Soprano::Statement> Nepomuk::ClassAndPropertyTree::simpleResourceToStatementList(const Nepomuk::SimpleResource &res) const
+{
+    const Soprano::Node subject = convertIfBlankNode(res.uri());
+    QList<Soprano::Statement> list;
+    PropertyHash properties = res.properties();
+    QHashIterator<QUrl, QVariant> it( properties );
+    while( it.hasNext() ) {
+        it.next();
+        const Soprano::Node object = variantToNode(it.value(), it.key());
+        list << Soprano::Statement(subject,
+                                   it.key(),
+                                   convertIfBlankNode(object));
+    }
+    return list;
+}
+
+QList<Soprano::Statement> Nepomuk::ClassAndPropertyTree::simpleResourceGraphToStatementList(const Nepomuk::SimpleResourceGraph &graph) const
+{
+    QList<Soprano::Statement> list;
+    foreach(const SimpleResource& res, graph.toList()) {
+        list += simpleResourceToStatementList(res);
+    }
+    return list;
+}
+
+QList<QUrl> Nepomuk::ClassAndPropertyTree::visibleTypes() const
+{
+    QList<QUrl> types;
+    QHash<QUrl, ClassOrProperty*>::const_iterator end = m_tree.constEnd();
+    for(QHash<QUrl, ClassOrProperty*>::const_iterator it = m_tree.constBegin(); it != end; ++it) {
+        const ClassOrProperty* cop = *it;
+        if(!cop->isProperty && cop->userVisible == 1) {
+            types << cop->uri;
+        }
+    }
+    return types;
+}
+
+Nepomuk::ClassAndPropertyTree * Nepomuk::ClassAndPropertyTree::self()
+{
+    return s_self;
+}
+
+#include "classandpropertytree.moc"
diff --git a/nepomuk/services/storage/classandpropertytree.h b/nepomuk/services/storage/classandpropertytree.h
new file mode 100644
index 0000000..af31091
--- /dev/null
+++ b/nepomuk/services/storage/classandpropertytree.h
@@ -0,0 +1,109 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CLASSANDPROPERTYTREE_H
+#define CLASSANDPROPERTYTREE_H
+
+#include <QtCore/QObject>
+#include <QtCore/QUrl>
+#include <QtCore/QVariant>
+#include <QtCore/QMutex>
+#include <QtCore/QList>
+
+#include <Soprano/Error/ErrorCache>
+
+namespace Soprano {
+class Model;
+class Node;
+class Statement;
+}
+
+
+namespace Nepomuk {
+
+class SimpleResource;
+class SimpleResourceGraph;
+
+class ClassAndPropertyTree : public QObject, public Soprano::Error::ErrorCache
+{
+    Q_OBJECT
+
+public:
+    ClassAndPropertyTree(QObject *parent = 0);
+    ~ClassAndPropertyTree();
+
+    /// \return \p true if \p uri is a class
+    bool isKnownClass(const QUrl& uri) const;
+
+    QSet<QUrl> allParents(const QUrl& uri) const;
+    bool isChildOf(const QUrl& type, const QUrl& superClass) const;
+
+    /// Returns true if the uri is a Class or a Property
+    bool contains(const QUrl& uri) const;
+
+    /**
+     * Returns true if any one of the uris in \p types is a child of \p superClass
+     */
+    bool isChildOf(const QList<QUrl> & types, const QUrl& superClass) const;
+    int maxCardinality(const QUrl& type) const;
+    bool isUserVisible(const QUrl& type) const;
+
+    QUrl propertyDomain(const QUrl& uri) const;
+    QUrl propertyRange(const QUrl& uri) const;
+
+    /// \return \p true if \p uri is a property and has a literal range, \p false otherwise.
+    bool hasLiteralRange(const QUrl& uri) const;
+
+    /// \return \p true if \p uri is an identifying property, \p false otherwise
+    bool isIdentifyingProperty(const QUrl& uri) const;
+
+    /// \return A list of all known rdf classes that are visible (nao:userVisible)
+    QList<QUrl> visibleTypes() const;
+
+    /// will try very hard to convert a variant into a node. Supports literal XML types and QUrl
+    Soprano::Node variantToNode(const QVariant& value, const QUrl& property) const;
+    QSet<Soprano::Node> variantListToNodeSet(const QVariantList& vl, const QUrl& property) const;
+
+    QList<Soprano::Statement> simpleResourceToStatementList(const Nepomuk::SimpleResource& res) const;
+    QList<Soprano::Statement> simpleResourceGraphToStatementList(const Nepomuk::SimpleResourceGraph& graph) const;
+
+    static ClassAndPropertyTree* self();
+
+public Q_SLOTS:
+    void rebuildTree(Soprano::Model* model);
+
+private:
+    class ClassOrProperty;
+
+    const ClassOrProperty* findClassOrProperty(const QUrl& uri) const;
+    int updateUserVisibility(ClassOrProperty *cop, QSet<QUrl> &visitedNodes);
+    int updateIdentifying(ClassOrProperty* cop, QSet<QUrl>& identifyingNodes);
+    QSet<QUrl> getAllParents(ClassOrProperty *cop, QSet<QUrl> &visitedNodes);
+
+    QHash<QUrl, ClassOrProperty*> m_tree;
+
+    mutable QMutex m_mutex;
+
+    static ClassAndPropertyTree* s_self;
+};
+}
+
+#endif
diff --git a/nepomuk/services/storage/crappyinferencer2.cpp b/nepomuk/services/storage/crappyinferencer2.cpp
index 95e5feb..1dbdf03 100644
--- a/nepomuk/services/storage/crappyinferencer2.cpp
+++ b/nepomuk/services/storage/crappyinferencer2.cpp
@@ -20,7 +20,7 @@
 */
 
 #include "crappyinferencer2.h"
-#include "typevisibilitytree.h"
+#include "classandpropertytree.h"
 
 #include <Soprano/Vocabulary/NRL>
 #include <Soprano/Vocabulary/NAO>
@@ -61,7 +61,7 @@ public:
     QSet<QUrl> buildSuperClassesHash( const QUrl& type, QSet<QUrl>& visitedTypes, const QMultiHash<QUrl, QUrl>& rdfsSubClassRelations );
 
     QHash<QUrl, QSet<QUrl> > m_superClasses;
-    TypeVisibilityTree* m_typeVisibilityTree;
+    Nepomuk::ClassAndPropertyTree* m_typeVisibilityTree;
     QMutex m_mutex;
 
     UpdateAllResourcesThread* m_updateAllResourcesThread;
@@ -102,6 +102,10 @@ public:
         // resources that do not have visibility set are treated as invisible, thus there is no need
         // to write their visibility value
         Q_FOREACH(const QUrl& type, m_model->d->m_typeVisibilityTree->visibleTypes()) {
+            if(m_canceled)
+                break;
+            if(m_model->d->m_typeVisibilityTree->isChildOf(type, Soprano::Vocabulary::NRL::Graph()))
+                continue;
             const QString query = QString::fromLatin1("insert into graph %1 { ?r %2 1 . } where { "
                                                       "graph ?g { ?r a %3 . } . "
                                                       "FILTER(?g!=%1) . "
@@ -117,12 +121,16 @@ public:
 #endif
 
         // update the resource type inference on all resources that were created before KDE 4.6.1
+        // we ignore graphs entirely to save space and allow the DMS to use SPARUL to remove graphs
         Soprano::QueryResultIterator it = m_model->executeQuery(QString::fromLatin1("select distinct ?t where { ?t a %1 . "
-                                                                                    "FILTER(bif:exists((select (1) where { graph ?g { ?r a ?t . } . FILTER(?g!=%2) . }))) . }")
+                                                                                    "FILTER(bif:exists((select (1) where { graph ?g { ?r a ?t . } . FILTER(?g!=%2) . }))) . "
+                                                                                    "FILTER(!bif:exists((select (1) where { ?t %3 %4 . }))) . }")
                                                                 .arg(Soprano::Node::resourceToN3(Soprano::Vocabulary::RDFS::Class()),
-                                                                     Soprano::Node::resourceToN3(m_model->crappyInferenceContext())),
+                                                                     Soprano::Node::resourceToN3(m_model->crappyInferenceContext()),
+                                                                     Soprano::Node::resourceToN3(Soprano::Vocabulary::RDFS::subClassOf()),
+                                                                     Soprano::Node::resourceToN3(Soprano::Vocabulary::NRL::Graph())),
                                                                 Soprano::Query::QueryLanguageSparql);
-        while(it.next()) {
+        while(!m_canceled && it.next()) {
             const QUrl type = it["t"].uri();
             const QSet<QUrl> superClasses = m_model->d->m_superClasses[type];
             QString superTypeTerms;
@@ -180,7 +188,7 @@ bool CrappyInferencer2::Private::isVisibleFromTypes(const QSet<QUrl> &types) con
 
     // if there is one leaf type left that is visible the resource is visible, too
     Q_FOREACH( const QUrl& type, leafTypes ) {
-        if( m_typeVisibilityTree->isVisible(type) ) {
+        if( m_typeVisibilityTree->isUserVisible(type) ) {
             return true;
         }
     }
@@ -226,13 +234,13 @@ QSet<QUrl> CrappyInferencer2::Private::buildSuperClassesHash( const QUrl& type,
     }
 }
 
-CrappyInferencer2::CrappyInferencer2(Soprano::Model* parent)
+CrappyInferencer2::CrappyInferencer2(Nepomuk::ClassAndPropertyTree* tree, Soprano::Model* parent)
     : Soprano::FilterModel(parent),
       d(new Private())
 {
     d->q = this;
     d->m_inferenceContext = QUrl::fromEncoded("urn:crappyinference2:inferredtriples");
-    d->m_typeVisibilityTree = new TypeVisibilityTree(this);
+    d->m_typeVisibilityTree = tree;
     if ( parent )
         updateInferenceIndex();
 }
@@ -268,11 +276,12 @@ Soprano::Error::ErrorCode CrappyInferencer2::addStatement(const Soprano::Stateme
     Soprano::Error::ErrorCode r = parentModel()->addStatement( statement );
     if( r == Soprano::Error::ErrorNone ) {
         //
-        // Handle the inference
+        // Handle the inference. However, we ignore inference for all graphs to save space and allow the DMS to use SPARUL there.
         //
         if ( statement.subject().isResource() &&
-                statement.object().isResource() &&
-                statement.predicate() == Soprano::Vocabulary::RDF::type() ) {
+             statement.object().isResource() &&
+             statement.predicate() == Soprano::Vocabulary::RDF::type() &&
+             !d->m_typeVisibilityTree->isChildOf(statement.object().uri(), Soprano::Vocabulary::NRL::Graph())) {
             addInferenceStatements( statement.subject().uri(), statement.object().uri() );
         }
     }
@@ -486,16 +495,12 @@ void CrappyInferencer2::updateInferenceIndex()
         rdfsResIt.value().insert(Soprano::Vocabulary::RDFS::Resource());
     }
 
-    // Build visibility tree
-    // ==============================================
-    d->m_typeVisibilityTree->rebuildTree();
-
 #ifndef NDEBUG
     // count for debugging
     int cnt = 0;
     for( QHash<QUrl, QSet<QUrl> >::const_iterator it = d->m_superClasses.constBegin();
          it != d->m_superClasses.constEnd(); ++it ) {
-        kDebug() << it.key() << "->" << it.value();
+        //kDebug() << it.key() << "->" << it.value();
         cnt += it.value().count();
     }
     kDebug() << "Number of classes:           " << d->m_superClasses.count();
@@ -525,7 +530,7 @@ void CrappyInferencer2::addInferenceStatements( const QUrl& resource, const QSet
     QSet<QUrl> superTypes;
     Q_FOREACH( const QUrl& type, types ) {
         superTypes += d->superClasses(type);
-        if( !haveVisibleType && d->m_typeVisibilityTree->isVisible(type) )
+        if( !haveVisibleType && d->m_typeVisibilityTree->isUserVisible(type) )
             haveVisibleType = true;
     }
     QSet<QUrl>::const_iterator end = superTypes.constEnd();
@@ -575,7 +580,7 @@ void CrappyInferencer2::removeInferenceStatements(const QUrl &resource, const QL
 
     bool haveVisibleType = false;
     Q_FOREACH( const QUrl& typeToRemove, typesToRemove ) {
-        if( !haveVisibleType && d->m_typeVisibilityTree->isVisible(typeToRemove) )
+        if( !haveVisibleType && d->m_typeVisibilityTree->isUserVisible(typeToRemove) )
             haveVisibleType = true;
         parentModel()->removeStatement(resource,
                                        Soprano::Vocabulary::RDF::type(),
diff --git a/nepomuk/services/storage/crappyinferencer2.h b/nepomuk/services/storage/crappyinferencer2.h
index b17726d..37c3598 100644
--- a/nepomuk/services/storage/crappyinferencer2.h
+++ b/nepomuk/services/storage/crappyinferencer2.h
@@ -24,6 +24,10 @@
 
 #include <Soprano/FilterModel>
 
+namespace Nepomuk {
+class ClassAndPropertyTree;
+}
+
 /**
  * The second version of the crappy inferencer it even crappier than the first.
  * It only does rdf:type inferencing, nothing else. In the long run this is not
@@ -38,7 +42,7 @@ class CrappyInferencer2 : public Soprano::FilterModel
     Q_OBJECT
 
 public:
-    CrappyInferencer2(Soprano::Model* parent = 0);
+    CrappyInferencer2(Nepomuk::ClassAndPropertyTree* tree, Soprano::Model* parent = 0);
     ~CrappyInferencer2();
 
     /**
diff --git a/nepomuk/services/storage/datamanagementadaptor.cpp b/nepomuk/services/storage/datamanagementadaptor.cpp
new file mode 100644
index 0000000..4ca8114
--- /dev/null
+++ b/nepomuk/services/storage/datamanagementadaptor.cpp
@@ -0,0 +1,225 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+
+   The basis for this file was generated by qdbusxml2cpp version 0.7
+   Command line was: qdbusxml2cpp -a datamanagementadaptor -c DataManagementAdaptor -m org.kde.nepomuk.DataManagement.xml
+
+   qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ */
+
+#include "datamanagementadaptor.h"
+#include "datamanagementmodel.h"
+#include "datamanagementcommand.h"
+#include "simpleresourcegraph.h"
+#include "dbustypes.h"
+
+#include <QtCore/QMetaObject>
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+#include <QtCore/QThreadPool>
+
+#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
+#include <Soprano/NRLModel>
+
+
+Nepomuk::DataManagementAdaptor::DataManagementAdaptor(Nepomuk::DataManagementModel *parent)
+    : QObject(parent),
+      m_model(parent),
+      m_namespacePrefixRx(QLatin1String("(\\w+)\\:(\\w+)"))
+{
+    DBus::registerDBusTypes();
+
+    m_threadPool = new QThreadPool(this);
+
+    // never let go of our threads - that is just pointless cpu cycles wasted
+    m_threadPool->setExpiryTimeout(-1);
+
+    // N threads means N connections to Virtuoso
+    m_threadPool->setMaxThreadCount(10);
+}
+
+Nepomuk::DataManagementAdaptor::~DataManagementAdaptor()
+{
+}
+
+void Nepomuk::DataManagementAdaptor::addProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new AddPropertyCommand(decodeUris(resources), decodeUri(property), Nepomuk::DBus::resolveDBusArguments(values), app, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::addProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
+{
+    addProperty(QStringList() << resource, property, QVariantList() << Nepomuk::DBus::resolveDBusArguments(value.variant()), app);
+}
+
+QString Nepomuk::DataManagementAdaptor::createResource(const QStringList &types, const QString &label, const QString &description, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new CreateResourceCommand(decodeUris(types), label, description, app, m_model, message()));
+    // QtDBus will ignore this return value
+    return QString();
+}
+
+QString Nepomuk::DataManagementAdaptor::createResource(const QString &type, const QString &label, const QString &description, const QString &app)
+{
+    return createResource(QStringList() << type, label, description, app);
+}
+
+QList<Nepomuk::SimpleResource> Nepomuk::DataManagementAdaptor::describeResources(const QStringList &resources, bool includeSubResources)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new DescribeResourcesCommand(decodeUris(resources), includeSubResources, m_model, message()));
+    // QtDBus will ignore this return value
+    return QList<SimpleResource>();
+}
+
+void Nepomuk::DataManagementAdaptor::storeResources(const QList<Nepomuk::SimpleResource>& resources, int identificationMode, int flags, const Nepomuk::PropertyHash &additionalMetadata, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new StoreResourcesCommand(resources, app, identificationMode, flags, additionalMetadata, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::mergeResources(const QString &resource1, const QString &resource2, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new MergeResourcesCommand(decodeUri(resource1), decodeUri(resource2), app, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::removeDataByApplication(int flags, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new RemoveDataByApplicationCommand(app, flags, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::removeDataByApplication(const QStringList &resources, int flags, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new RemoveResourcesByApplicationCommand(decodeUris(resources), app, flags, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::removeProperties(const QStringList &resources, const QStringList &properties, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new RemovePropertiesCommand(decodeUris(resources), decodeUris(properties), app, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::removeProperties(const QString &resource, const QString &property, const QString &app)
+{
+    removeProperties(QStringList() << resource, QStringList() << property, app);
+}
+
+void Nepomuk::DataManagementAdaptor::removeProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new RemovePropertyCommand(decodeUris(resources), decodeUri(property), Nepomuk::DBus::resolveDBusArguments(values), app, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::removeProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
+{
+    removeProperty(QStringList() << resource, property, QVariantList() << Nepomuk::DBus::resolveDBusArguments(value.variant()), app);
+}
+
+void Nepomuk::DataManagementAdaptor::removeResources(const QStringList &resources, int flags, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new RemoveResourcesCommand(decodeUris(resources), app, flags, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::removeResources(const QString &resource, int flags, const QString &app)
+{
+    removeResources(QStringList() << resource, flags, app);
+}
+
+void Nepomuk::DataManagementAdaptor::setProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new SetPropertyCommand(decodeUris(resources), decodeUri(property), Nepomuk::DBus::resolveDBusArguments(values), app, m_model, message()));
+}
+
+void Nepomuk::DataManagementAdaptor::setProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
+{
+    setProperty(QStringList() << resource, property, QVariantList() << Nepomuk::DBus::resolveDBusArguments(value.variant()), app);
+}
+
+void Nepomuk::DataManagementAdaptor::enqueueCommand(DataManagementCommand *cmd)
+{
+    m_threadPool->start(cmd);
+}
+
+QUrl Nepomuk::DataManagementAdaptor::decodeUri(const QString &s, bool namespaceAbbrExpansion) const
+{
+    if(namespaceAbbrExpansion) {
+        if(m_namespacePrefixRx.exactMatch(s)) {
+            const QString ns = m_namespacePrefixRx.cap(1);
+            const QString name = m_namespacePrefixRx.cap(2);
+            QHash<QString, QString>::const_iterator it = m_namespaces.constFind(ns);
+            if(it != m_namespaces.constEnd()) {
+                return QUrl::fromEncoded(QString(it.value() + name).toAscii());
+            }
+        }
+    }
+
+    // fallback
+    return Nepomuk::decodeUrl(s);
+}
+
+QList<QUrl> Nepomuk::DataManagementAdaptor::decodeUris(const QStringList &urlStrings, bool namespaceAbbrExpansions) const
+{
+    QList<QUrl> urls;
+    Q_FOREACH(const QString& urlString, urlStrings) {
+        urls << decodeUri(urlString, namespaceAbbrExpansions);
+    }
+    return urls;
+}
+
+void Nepomuk::DataManagementAdaptor::setPrefixes(const QHash<QString, QString>& prefixes)
+{
+    m_namespaces = prefixes;
+}
+
+void Nepomuk::DataManagementAdaptor::importResources(const QString &url, const QString &serialization, int identificationMode, int flags, const QString &app)
+{
+    importResources(url, serialization, identificationMode, flags, PropertyHash(), app);
+}
+
+void Nepomuk::DataManagementAdaptor::importResources(const QString &url, const QString &serialization, int identificationMode, int flags, const Nepomuk::PropertyHash &additionalMetadata, const QString &app)
+{
+    Q_ASSERT(calledFromDBus());
+    setDelayedReply(true);
+    enqueueCommand(new ImportResourcesCommand(decodeUri(url), Soprano::mimeTypeToSerialization(serialization), serialization, identificationMode, flags, additionalMetadata, app, m_model, message()));
+}
+
+#include "datamanagementadaptor.moc"
diff --git a/nepomuk/services/storage/datamanagementadaptor.h b/nepomuk/services/storage/datamanagementadaptor.h
new file mode 100644
index 0000000..05ef64c
--- /dev/null
+++ b/nepomuk/services/storage/datamanagementadaptor.h
@@ -0,0 +1,105 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+
+   The basis for this file was generated by qdbusxml2cpp version 0.7
+   Command line was: qdbusxml2cpp -a datamanagementadaptor -c DataManagementAdaptor -m org.kde.nepomuk.DataManagement.xml
+
+   qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ */
+
+#ifndef _DATAMANAGEMENTADAPTOR_H_
+#define _DATAMANAGEMENTADAPTOR_H_
+
+#include <QtCore/QObject>
+#include <QtCore/QHash>
+#include <QtCore/QRegExp>
+#include <QtDBus/QDBusContext>
+#include <QtDBus/QDBusVariant>
+
+#include "simpleresource.h"
+
+class QThreadPool;
+
+namespace Nepomuk {
+class DataManagementModel;
+class DataManagementCommand;
+
+/*
+ * Adaptor class for interface org.kde.nepomuk.DataManagement
+ */
+class DataManagementAdaptor: public QObject, protected QDBusContext
+{
+    Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", "org.kde.nepomuk.DataManagement")
+
+public:
+    DataManagementAdaptor(Nepomuk::DataManagementModel* parent);
+    ~DataManagementAdaptor();
+
+    /**
+     * Set the prefixes that will be supported for script convenience. The provided hash
+     * maps prefixes to namespaces. Typical prefixes include "nao" or "rdfs".
+     */
+    void setPrefixes(const QHash<QString, QString>& prefixes);
+
+    /**
+     * Tries to decode a URI including namespace abbreviation lookup for known ontologies (Example: nao:Tag).
+     */
+    QUrl decodeUri(const QString& s, bool namespaceAbbrExpansion = true) const;
+
+    /**
+     * Tries to decode a list of URIs including namespace abbreviation lookup for known ontologies (Example: nao:Tag).
+     */
+    QList<QUrl> decodeUris(const QStringList& s, bool namespaceAbbrExpansion = true) const;
+
+public Q_SLOTS:
+    Q_SCRIPTABLE void setProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app);
+    Q_SCRIPTABLE void addProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app);
+    Q_SCRIPTABLE void removeProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app);
+    Q_SCRIPTABLE void removeProperties(const QStringList &resources, const QStringList &properties, const QString &app);
+    Q_SCRIPTABLE QString createResource(const QStringList &types, const QString &label, const QString &description, const QString &app);
+    Q_SCRIPTABLE void removeResources(const QStringList &resources, int flags, const QString &app);
+    Q_SCRIPTABLE QList<Nepomuk::SimpleResource> describeResources(const QStringList &resources, bool includeSubResources);
+    Q_SCRIPTABLE void storeResources(const QList<Nepomuk::SimpleResource>& resources, int identificationMode, int flags, const Nepomuk::PropertyHash &additionalMetadata, const QString &app);
+    Q_SCRIPTABLE void mergeResources(const QString &resource1, const QString &resource2, const QString &app);
+    Q_SCRIPTABLE void removeDataByApplication(int flags, const QString &app);
+    Q_SCRIPTABLE void removeDataByApplication(const QStringList &resources, int flags, const QString &app);
+    Q_SCRIPTABLE void importResources(const QString& url, const QString& serialization, int identificationMode, int flags, const Nepomuk::PropertyHash &additionalMetadata, const QString& app);
+
+    /// convinience overloads for scripts (no lists)
+    Q_SCRIPTABLE void setProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app);
+    Q_SCRIPTABLE void addProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app);
+    Q_SCRIPTABLE void removeProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app);
+    Q_SCRIPTABLE void removeProperties(const QString &resource, const QString &property, const QString &app);
+    Q_SCRIPTABLE QString createResource(const QString &type, const QString &label, const QString &description, const QString &app);
+    Q_SCRIPTABLE void removeResources(const QString &resource, int flags, const QString &app);
+    Q_SCRIPTABLE void importResources(const QString& url, const QString& serialization, int identificationMode, int flags, const QString& app);
+
+private:
+    void enqueueCommand(Nepomuk::DataManagementCommand* cmd);
+
+    Nepomuk::DataManagementModel* m_model;
+    QThreadPool* m_threadPool;
+    QHash<QString, QString> m_namespaces;
+    QRegExp m_namespacePrefixRx;
+};
+}
+
+#endif
diff --git a/nepomuk/services/storage/datamanagementcommand.cpp b/nepomuk/services/storage/datamanagementcommand.cpp
new file mode 100644
index 0000000..7cfc323
--- /dev/null
+++ b/nepomuk/services/storage/datamanagementcommand.cpp
@@ -0,0 +1,115 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "datamanagementcommand.h"
+#include "datamanagementmodel.h"
+
+#include <Soprano/Error/Error>
+#include <Soprano/Error/ErrorCode>
+
+#include <QtDBus/QDBusConnection>
+
+#include <QtCore/QStringList>
+#include <QtCore/QEventLoop>
+
+#include <KUrl>
+
+
+namespace {
+QDBusError::ErrorType convertSopranoErrorCode(int code)
+{
+    switch(code) {
+    case Soprano::Error::ErrorInvalidArgument:
+        return QDBusError::InvalidArgs;
+    default:
+        return QDBusError::Failed;
+    }
+}
+}
+
+
+Nepomuk::DataManagementCommand::DataManagementCommand(DataManagementModel* model, const QDBusMessage& msg)
+    : QRunnable(),
+      m_model(model),
+      m_msg(msg)
+{
+}
+
+Nepomuk::DataManagementCommand::~DataManagementCommand()
+{
+}
+
+void Nepomuk::DataManagementCommand::run()
+{
+    QVariant result = runCommand();
+    Soprano::Error::Error error = model()->lastError();
+    if(error) {
+        // send error reply
+        QDBusConnection::sessionBus().send(m_msg.createErrorReply(convertSopranoErrorCode(error.code()), error.message()));
+    }
+    else {
+        // encode result (ie. convert QUrl to QString)
+        if(result.isValid()) {
+            if(result.type() == QVariant::Url) {
+                result = encodeUrl(result.toUrl());
+            }
+            QDBusConnection::sessionBus().send(m_msg.createReply(result));
+        }
+        else {
+            QDBusConnection::sessionBus().send(m_msg.createReply());
+        }
+    }
+
+    //
+    // DBus requires event handling for signals to be emitted properly.
+    // (for example the Soprano statement signals which are emitted a
+    // lot during command execution.)
+    // Otherwise memory will fill up with queued DBus message objects.
+    // Instead of executing an event loop we avoid all the hassle and
+    // simply handle all events here.
+    //
+    QEventLoop loop;
+    loop.processEvents();
+}
+
+
+// static
+QUrl Nepomuk::decodeUrl(const QString& urlsString)
+{
+    // we use the power of KUrl to automatically convert file paths to file:/ URLs
+    return KUrl(urlsString);
+}
+
+// static
+QList<QUrl> Nepomuk::decodeUrls(const QStringList& urlStrings)
+{
+    QList<QUrl> urls;
+    Q_FOREACH(const QString& urlString, urlStrings) {
+        urls << decodeUrl(urlString);
+    }
+    return urls;
+}
+
+// static
+QString Nepomuk::encodeUrl(const QUrl& url)
+{
+    return QString::fromAscii(url.toEncoded());
+}
diff --git a/nepomuk/services/storage/datamanagementcommand.h b/nepomuk/services/storage/datamanagementcommand.h
new file mode 100644
index 0000000..c970404
--- /dev/null
+++ b/nepomuk/services/storage/datamanagementcommand.h
@@ -0,0 +1,377 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DATAMANAGEMENTCOMMAND_H
+#define DATAMANAGEMENTCOMMAND_H
+
+#include <QRunnable>
+#include <QtCore/QVariant>
+#include <QtDBus/QDBusMessage>
+
+#include "lib/dbustypes.h"
+#include "lib/simpleresource.h"
+#include "lib/simpleresourcegraph.h"
+#include "lib/datamanagement.h"
+#include "datamanagementmodel.h"
+
+
+namespace Nepomuk {
+
+QUrl decodeUrl(const QString& urlsString);
+QList<QUrl> decodeUrls(const QStringList& urlStrings);
+QString encodeUrl(const QUrl& url);
+
+class DataManagementCommand : public QRunnable
+{
+public:
+    DataManagementCommand(DataManagementModel* model, const QDBusMessage& msg);
+    virtual ~DataManagementCommand();
+
+    void run();
+
+    DataManagementModel* model() const { return m_model; }
+
+protected:
+    /**
+     * Reimplement this method. Return the original value from the call.
+     * It will be properly converted automatically.
+     * Error handling is done automatically, too.
+     * Only method calls to model() are supported.
+     */
+    virtual QVariant runCommand() = 0;
+
+private:
+    DataManagementModel* m_model;
+    QDBusMessage m_msg;
+};
+
+class AddPropertyCommand : public DataManagementCommand
+{
+public:
+    AddPropertyCommand(const QList<QUrl>& res,
+                       const QUrl& property,
+                       const QVariantList& values,
+                       const QString& app,
+                       Nepomuk::DataManagementModel* model,
+                       const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(res),
+          m_property(property),
+          m_values(values),
+          m_app(app) {}
+
+private:
+    QVariant runCommand() {
+        model()->addProperty(m_resources, m_property, m_values, m_app);
+        return QVariant();
+    }
+
+    QList<QUrl> m_resources;
+    QUrl m_property;
+    QVariantList m_values;
+    QString m_app;
+};
+
+class SetPropertyCommand : public DataManagementCommand
+{
+public:
+    SetPropertyCommand(const QList<QUrl>& res,
+                       const QUrl& property,
+                       const QVariantList& values,
+                       const QString& app,
+                       Nepomuk::DataManagementModel* model,
+                       const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(res),
+          m_property(property),
+          m_values(values),
+          m_app(app) {}
+
+private:
+    QVariant runCommand() {
+        model()->setProperty(m_resources, m_property, m_values, m_app);
+        return QVariant();
+    }
+
+    QList<QUrl> m_resources;
+    QUrl m_property;
+    QVariantList m_values;
+    QString m_app;
+};
+
+class CreateResourceCommand : public DataManagementCommand
+{
+public:
+    CreateResourceCommand(const QList<QUrl>& resources,
+                          const QString& label,
+                          const QString& desc,
+                          const QString& app,
+                          Nepomuk::DataManagementModel* model,
+                          const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(resources),
+          m_label(label),
+          m_desc(desc),
+          m_app(app) {}
+
+private:
+    QVariant runCommand() {
+        return model()->createResource(m_resources, m_label, m_desc, m_app);
+    }
+
+    QList<QUrl> m_resources;
+    QString m_label;
+    QString m_desc;
+    QString m_app;
+};
+
+class StoreResourcesCommand : public DataManagementCommand
+{
+public:
+    StoreResourcesCommand(const SimpleResourceGraph& resources,
+                          const QString& app,
+                          int identificationMode,
+                          int flags,
+                          const QHash<QUrl, QVariant>& additionalMetadata,
+                          Nepomuk::DataManagementModel* model,
+                          const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(resources),
+          m_app(app),
+          m_identificationMode(Nepomuk::StoreIdentificationMode(identificationMode)),
+          m_flags(flags),
+          m_additionalMetadata(additionalMetadata) {}
+
+private:
+    QVariant runCommand() {
+        model()->storeResources(m_resources, m_app, m_identificationMode, m_flags, m_additionalMetadata);
+        return QVariant();
+    }
+
+    SimpleResourceGraph m_resources;
+    QString m_app;
+    Nepomuk::StoreIdentificationMode m_identificationMode;
+    Nepomuk::StoreResourcesFlags m_flags;
+    QHash<QUrl, QVariant> m_additionalMetadata;
+};
+
+class MergeResourcesCommand : public DataManagementCommand
+{
+public:
+    MergeResourcesCommand(const QUrl& resource1,
+                          const QUrl& resource2,
+                          const QString& app,
+                          Nepomuk::DataManagementModel* model,
+                          const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resource1(resource1),
+          m_resource2(resource2),
+          m_app(app) {}
+
+private:
+    QVariant runCommand() {
+        model()->mergeResources(m_resource1, m_resource2, m_app);
+        return QVariant();
+    }
+
+    QUrl m_resource1;
+    QUrl m_resource2;
+    QString m_app;
+};
+
+class RemoveResourcesByApplicationCommand : public DataManagementCommand
+{
+public:
+    RemoveResourcesByApplicationCommand(const QList<QUrl>& res,
+                                        const QString& app,
+                                        int flags,
+                                        Nepomuk::DataManagementModel* model,
+                                        const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(res),
+          m_app(app),
+          m_flags(flags) {}
+
+private:
+    QVariant runCommand() {
+        model()->removeDataByApplication(m_resources, m_flags, m_app);
+        return QVariant();
+    }
+
+    QList<QUrl> m_resources;
+    QString m_app;
+    Nepomuk::RemovalFlags m_flags;
+};
+
+class RemoveDataByApplicationCommand : public DataManagementCommand
+{
+public:
+    RemoveDataByApplicationCommand(const QString& app,
+                                   int flags,
+                                   Nepomuk::DataManagementModel* model,
+                                   const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_app(app),
+          m_flags(flags) {}
+
+private:
+    QVariant runCommand() {
+        model()->removeDataByApplication(m_flags, m_app);
+        return QVariant();
+    }
+
+    QString m_app;
+    Nepomuk::RemovalFlags m_flags;
+};
+
+class RemovePropertiesCommand : public DataManagementCommand
+{
+public:
+    RemovePropertiesCommand(const QList<QUrl>& res,
+                            const QList<QUrl>& properties,
+                            const QString& app,
+                            Nepomuk::DataManagementModel* model,
+                            const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(res),
+          m_properties(properties),
+          m_app(app) {}
+
+private:
+    QVariant runCommand() {
+        model()->removeProperties(m_resources, m_properties, m_app);
+        return QVariant();
+    }
+
+    QList<QUrl> m_resources;
+    QList<QUrl> m_properties;
+    QString m_app;
+};
+
+class RemovePropertyCommand : public DataManagementCommand
+{
+public:
+    RemovePropertyCommand(const QList<QUrl>& res,
+                          const QUrl& property,
+                          const QVariantList& values,
+                          const QString& app,
+                          Nepomuk::DataManagementModel* model,
+                          const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(res),
+          m_property(property),
+          m_values(values),
+          m_app(app) {}
+
+private:
+    QVariant runCommand() {
+        model()->removeProperty(m_resources, m_property, m_values, m_app);
+        return QVariant();
+    }
+
+    QList<QUrl> m_resources;
+    QUrl m_property;
+    QVariantList m_values;
+    QString m_app;
+};
+
+class RemoveResourcesCommand : public DataManagementCommand
+{
+public:
+    RemoveResourcesCommand(const QList<QUrl>& res,
+                           const QString& app,
+                           int flags,
+                           Nepomuk::DataManagementModel* model,
+                           const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(res),
+          m_app(app),
+          m_flags(flags) {}
+
+private:
+    QVariant runCommand() {
+        model()->removeResources(m_resources, m_flags, m_app);
+        return QVariant();
+    }
+
+    QList<QUrl> m_resources;
+    QString m_app;
+    Nepomuk::RemovalFlags m_flags;
+};
+
+class DescribeResourcesCommand : public DataManagementCommand
+{
+public:
+    DescribeResourcesCommand(const QList<QUrl>& res,
+                             bool includeSubResources,
+                             Nepomuk::DataManagementModel* model,
+                             const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_resources(res),
+          m_includeSubResources(includeSubResources) {}
+
+private:
+    QVariant runCommand() {
+        return QVariant::fromValue(model()->describeResources(m_resources, m_includeSubResources).toList());
+    }
+
+    QList<QUrl> m_resources;
+    bool m_includeSubResources;
+};
+
+class ImportResourcesCommand : public DataManagementCommand
+{
+public:
+    ImportResourcesCommand(const QUrl& url,
+                           Soprano::RdfSerialization serialization,
+                           const QString& userSerialization,
+                           int identificationMode,
+                           int flags,
+                           const PropertyHash& additionalProps,
+                           const QString& app,
+                           Nepomuk::DataManagementModel* model,
+                           const QDBusMessage& msg)
+        : DataManagementCommand(model, msg),
+          m_url(url),
+          m_serialization(serialization),
+          m_userSerialization(userSerialization),
+          m_identificationMode(Nepomuk::StoreIdentificationMode(identificationMode)),
+          m_flags(flags),
+          m_additionalProperties(additionalProps),
+          m_app(app) {}
+
+private:
+    QVariant runCommand() {
+        model()->importResources(m_url, m_app, m_serialization, m_userSerialization, m_identificationMode, m_flags, m_additionalProperties);
+        return QVariant();
+    }
+
+    QUrl m_url;
+    Soprano::RdfSerialization m_serialization;
+    QString m_userSerialization;
+    Nepomuk::StoreIdentificationMode m_identificationMode;
+    Nepomuk::StoreResourcesFlags m_flags;
+    PropertyHash m_additionalProperties;
+    QString m_app;
+};
+}
+
+#endif
diff --git a/nepomuk/services/storage/datamanagementmodel.cpp b/nepomuk/services/storage/datamanagementmodel.cpp
new file mode 100644
index 0000000..af653f9
--- /dev/null
+++ b/nepomuk/services/storage/datamanagementmodel.cpp
@@ -0,0 +1,2590 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "datamanagementmodel.h"
+#include "classandpropertytree.h"
+#include "resourcemerger.h"
+#include "resourceidentifier.h"
+#include "simpleresourcegraph.h"
+#include "simpleresource.h"
+#include "resourcewatchermanager.h"
+#include "syncresource.h"
+#include "nepomuktools.h"
+
+#include <Soprano/Vocabulary/NRL>
+#include <Soprano/Vocabulary/NAO>
+#include <Soprano/Vocabulary/RDF>
+#include <Soprano/Vocabulary/RDFS>
+
+#include <Soprano/Graph>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/StatementIterator>
+#include <Soprano/NodeIterator>
+#include <Soprano/Error/ErrorCode>
+#include <Soprano/Parser>
+#include <Soprano/PluginManager>
+
+#include <QtCore/QHash>
+#include <QtCore/QUrl>
+#include <QtCore/QVariant>
+#include <QtCore/QDateTime>
+#include <QtCore/QUuid>
+#include <QtCore/QSet>
+#include <QtCore/QPair>
+#include <QtCore/QFileInfo>
+
+#include <Nepomuk/Vocabulary/NIE>
+#include <Nepomuk/Vocabulary/NFO>
+
+#include <KDebug>
+#include <KService>
+#include <KServiceTypeTrader>
+#include <KProtocolInfo>
+
+#include <KIO/NetAccess>
+
+#define STRIGI_INDEX_GRAPH_FOR "http://www.strigi.org/fields#indexGraphFor"
+
+using namespace Nepomuk::Vocabulary;
+using namespace Soprano::Vocabulary;
+
+//// TODO: do not allow to create properties or classes through the "normal" methods. Instead provide methods for it.
+//// IDEAS:
+//// 1. Somehow handle nie:hasPart (at least in describeResources - compare text annotations where we only want to annotate part of a text)
+
+namespace {
+    /// convert a hash of URL->URI mappings to N3, omitting empty URIs.
+    /// This is a helper for the return type of DataManagementModel::resolveUrls
+    QStringList resourceHashToN3(const QHash<QUrl, QUrl>& urls) {
+        QStringList n3;
+        QHash<QUrl, QUrl>::const_iterator end = urls.constEnd();
+        for(QHash<QUrl, QUrl>::const_iterator it = urls.constBegin(); it != end; ++it) {
+            if(!it.value().isEmpty())
+                n3 << Soprano::Node::resourceToN3(it.value());
+        }
+        return n3;
+    }
+
+    QStringList nodesToN3(const QSet<Soprano::Node>& nodes) {
+        QStringList n3;
+        Q_FOREACH(const Soprano::Node& node, nodes) {
+            n3 << node.toN3();
+        }
+        return n3;
+    }
+
+    template<typename T> QString createResourceFilter(const T& resources, const QString& var, bool exclude = true) {
+        QString filter = QString::fromLatin1("%1 in (%2)").arg(var, Nepomuk::resourcesToN3(resources).join(QLatin1String(",")));
+        if(exclude) {
+            filter = QString::fromLatin1("!(%1)").arg(filter);
+        }
+        return filter;
+    }
+
+    /*
+     * Creates a filter (without the "FILTER" keyword) which either excludes the \p propVar
+     * as once of the metadata properties or forces it to be one.
+     */
+    QString createResourceMetadataPropertyFilter(const QString& propVar, bool exclude = true) {
+        return createResourceFilter(QList<QUrl>()
+                                    << NAO::created()
+                                    << NAO::lastModified()
+                                    << NAO::creator()
+                                    << NAO::userVisible()
+                                    << NIE::url(),
+                                    propVar,
+                                    exclude);
+    }
+
+    enum UriState {
+        /// the URI is a URL which points to an existing local file
+        ExistingFileUrl,
+        /// the URI is a file URL which points to a non-existing local file
+        NonExistingFileUrl,
+        /// the URI is a URL which uses on of the protocols supported by KIO
+        SupportedUrl,
+        /// the URI is a nepomuk:/ URI
+        NepomukUri,
+        /// A uri of the form "_:id"
+        BlankUri,
+        /// A uri in the ontology
+        OntologyUri,
+        /// the URI is some other non-supported URI
+        OtherUri
+    };
+
+    /// Check if a URL points to a local file. This should be the only place where the local file is stat'ed
+    inline UriState uriState(const QUrl& uri, bool statLocalFiles = true) {
+        if(uri.scheme() == QLatin1String("nepomuk")) {
+            return NepomukUri;
+        }
+        else if(uri.scheme() == QLatin1String("file")) {
+            if(!statLocalFiles ||
+                    QFile::exists(uri.toLocalFile())) {
+                return ExistingFileUrl;
+            }
+            else {
+                return NonExistingFileUrl;
+            }
+        }
+        else if(Nepomuk::ClassAndPropertyTree::self()->contains(uri)) {
+            return OntologyUri;
+        }
+        // if supported by kio
+        else if( KProtocolInfo::isKnownProtocol(uri) ) {
+            return SupportedUrl;
+        }
+        else if(uri.toString().startsWith("_:") ) {
+            return BlankUri;
+        }
+        else {
+            return OtherUri;
+        }
+    }
+
+    inline Soprano::Node convertIfBlankUri(const QUrl &uri) {
+        if( uri.toString().startsWith(QLatin1String("_:")) )
+            return Soprano::Node( uri.toString().mid(2) );
+        else
+            return Soprano::Node( uri );
+    }
+}
+
+class Nepomuk::DataManagementModel::Private
+{
+public:
+    ClassAndPropertyTree* m_classAndPropertyTree;
+    ResourceWatcherManager* m_watchManager;
+
+    /// a set of properties that are maintained by the service and cannot be changed by clients
+    QSet<QUrl> m_protectedProperties;
+};
+
+Nepomuk::DataManagementModel::DataManagementModel(Nepomuk::ClassAndPropertyTree* tree, Soprano::Model* model, QObject *parent)
+    : Soprano::FilterModel(model),
+      d(new Private())
+{
+    d->m_classAndPropertyTree = tree;
+    d->m_watchManager = new ResourceWatcherManager(this);
+
+    setParent(parent);
+
+    // meta data properties are protected. This means they cannot be removed. But they
+    // can be set.
+    d->m_protectedProperties.insert(NAO::created());
+    d->m_protectedProperties.insert(NAO::creator());
+    d->m_protectedProperties.insert(NAO::lastModified());
+    d->m_protectedProperties.insert(NAO::userVisible());
+    d->m_protectedProperties.insert(NIE::url());
+}
+
+Nepomuk::DataManagementModel::~DataManagementModel()
+{
+    delete d;
+}
+
+
+Soprano::Error::ErrorCode Nepomuk::DataManagementModel::updateModificationDate(const QUrl& resource, const QUrl & graph, const QDateTime& date, bool includeCreationDate)
+{
+    return updateModificationDate(QSet<QUrl>() << resource, graph, date, includeCreationDate);
+}
+
+
+Soprano::Error::ErrorCode Nepomuk::DataManagementModel::updateModificationDate(const QSet<QUrl>& resources, const QUrl & graph, const QDateTime& date, bool includeCreationDate)
+{
+    if(resources.isEmpty()) {
+        return Soprano::Error::ErrorNone;
+    }
+
+    QUrl metadataGraph(graph);
+    if(metadataGraph.isEmpty()) {
+        metadataGraph = createGraph();
+    }
+
+    QSet<QUrl> mtimeGraphs;
+    Soprano::QueryResultIterator it = executeQuery(QString::fromLatin1("select distinct ?g where { graph ?g { ?r %1 ?d . FILTER(?r in (%2)) . } . }")
+                                                   .arg(Soprano::Node::resourceToN3(NAO::lastModified()),
+                                                        resourcesToN3(resources).join(QLatin1String(","))),
+                                                   Soprano::Query::QueryLanguageSparql);
+    while(it.next()) {
+        mtimeGraphs << it[0].uri();
+    }
+
+    foreach(const QUrl& resource, resources) {
+        Soprano::Error::ErrorCode c = removeAllStatements(resource, NAO::lastModified(), Soprano::Node());
+        if (c != Soprano::Error::ErrorNone)
+            return c;
+        addStatement(resource, NAO::lastModified(), Soprano::LiteralValue( date ), metadataGraph);
+        if(includeCreationDate && !containsAnyStatement(resource, NAO::created(), Soprano::Node())) {
+            addStatement(resource, NAO::created(), Soprano::LiteralValue( date ), metadataGraph);
+        }
+    }
+
+    removeTrailingGraphs(mtimeGraphs);
+
+    return Soprano::Error::ErrorNone;
+}
+
+void Nepomuk::DataManagementModel::addProperty(const QList<QUrl> &resources, const QUrl &property, const QVariantList &values, const QString &app)
+{
+    //
+    // Check parameters
+    //
+    if(app.isEmpty()) {
+        setError(QLatin1String("addProperty: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(resources.isEmpty()) {
+        setError(QLatin1String("addProperty: No resource specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    foreach( const QUrl & res, resources ) {
+        if(res.isEmpty()) {
+            setError(QLatin1String("addProperty: Encountered empty resource URI."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+    if(property.isEmpty()) {
+        setError(QLatin1String("addProperty: Property needs to be specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(values.isEmpty()) {
+        setError(QLatin1String("addProperty: Values needs to be specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(d->m_protectedProperties.contains(property)) {
+        setError(QString::fromLatin1("addProperty: %1 is a protected property which can only be set.").arg(property.toString()),
+                 Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+
+
+    //
+    // Convert all values to RDF nodes. This includes the property range check and conversion of local file paths to file URLs
+    //
+    const QSet<Soprano::Node> nodes = d->m_classAndPropertyTree->variantListToNodeSet(values, property);
+    if(nodes.isEmpty()) {
+        setError(d->m_classAndPropertyTree->lastError());
+        return;
+    }
+
+
+    //
+    // We need to ensure that no client removes any ontology constructs or graphs,
+    // we can check this before resolving file URLs since no protected resource will
+    // ever have a nie:url
+    //
+    if(containsResourceWithProtectedType(QSet<QUrl>::fromList(resources))) {
+        return;
+    }
+
+
+    clearError();
+
+
+    //
+    // Hash to keep mapping from provided URL/URI to resource URIs
+    //
+    QHash<Soprano::Node, Soprano::Node> resolvedNodes;
+
+
+    if(property == NIE::url()) {
+        if(resources.count() != 1) {
+            setError(QLatin1String("addProperty: no two resources can have the same nie:url."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+        else if(nodes.count() > 1) {
+            setError(QLatin1String("addProperty: One resource can only have one nie:url."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+
+        if(!nodes.isEmpty()) {
+            // check if another resource already uses the URL - no two resources can have the same URL at the same time
+            // CAUTION: There is one theoretical situation in which this breaks (more than this actually):
+            //          A file is moved and before the nie:url is updated data is added to the file in the new location.
+            //          At this point the file is there twice and the data should ideally be merged. But how to decide that
+            //          and how to distiguish between that situation and a file overwrite?
+            if(containsAnyStatement(Soprano::Node(), NIE::url(), *nodes.constBegin())) {
+                setError(QLatin1String("addProperty: No two resources can have the same nie:url at the same time."), Soprano::Error::ErrorInvalidArgument);
+                return;
+            }
+            else if(containsAnyStatement(resources.first(), NIE::url(), Soprano::Node())) {
+                // TODO: this can be removed as soon as nie:url gets a max cardinality of 1
+                // vHanda: nie:url as of KDE 4.6 has a max cardinality of 1. This is due to the
+                //         nepomuk resource identification ontology
+                setError(QLatin1String("addProperty: One resource can only have one nie:url."), Soprano::Error::ErrorInvalidArgument);
+                return;
+            }
+
+            // nie:url is the only property for which we do not want to resolve URLs
+            resolvedNodes.insert(*nodes.constBegin(), *nodes.constBegin());
+        }
+    }
+    else {
+        resolvedNodes = resolveNodes(nodes);
+        if(lastError()) {
+            return;
+        }
+    }
+
+
+    //
+    // Resolve local file URLs (we need to hash the values since we do not want to write anything yet)
+    //
+    QHash<QUrl, QUrl> uriHash = resolveUrls(resources);
+    if(lastError()) {
+        return;
+    }
+
+
+    //
+    // Check cardinality conditions
+    //
+    const int maxCardinality = d->m_classAndPropertyTree->maxCardinality(property);
+    if( maxCardinality == 1 ) {
+        // check if any of the resources already has a value set which differs from the one we want to add
+
+        // an empty hashed value means that the resource for a file URL does not exist yet. Thus, there is
+        // no need to filter it out. We basically only need to check if any value exists.
+        QString valueFilter;
+        if(resolvedNodes.constBegin().value().isValid()) {
+            valueFilter = QString::fromLatin1("FILTER(?v!=%3) . ")
+                    .arg(resolvedNodes.constBegin().value().toN3());
+        }
+
+        QStringList terms;
+        Q_FOREACH(const QUrl& res, resources) {
+            if(!uriHash[res].isEmpty()) {
+                terms << QString::fromLatin1("%1 %2 ?v . %3")
+                         .arg(Soprano::Node::resourceToN3(uriHash[res]),
+                              Soprano::Node::resourceToN3(property),
+                              valueFilter);
+            }
+        }
+
+        const QString cardinalityQuery = QString::fromLatin1("ask where { { %1 } }")
+                .arg(terms.join(QLatin1String("} UNION {")));
+
+        if(executeQuery(cardinalityQuery, Soprano::Query::QueryLanguageSparql).boolValue()) {
+            setError(QString::fromLatin1("%1 has cardinality of 1. At least one of the resources already has a value set that differs from the new one.")
+                     .arg(Soprano::Node::resourceToN3(property)), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+
+
+    //
+    // Do the actual work
+    //
+    addProperty(uriHash, property, resolvedNodes, app);
+}
+
+
+// setting a property can be implemented by way of addProperty. All we have to do before calling addProperty is to remove all
+// the values that are not in the setProperty call
+void Nepomuk::DataManagementModel::setProperty(const QList<QUrl> &resources, const QUrl &property, const QVariantList &values, const QString &app)
+{
+    //
+    // Special case: setting to the empty list
+    //
+    if(values.isEmpty()) {
+        removeProperties(resources, QList<QUrl>() << property, app);
+        return;
+    }
+
+    //
+    // Check parameters
+    //
+    if(app.isEmpty()) {
+        setError(QLatin1String("setProperty: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(resources.isEmpty()) {
+        setError(QLatin1String("setProperty: No resource specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    foreach( const QUrl & res, resources ) {
+        if(res.isEmpty()) {
+            setError(QLatin1String("setProperty: Encountered empty resource URI."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+    if(property.isEmpty()) {
+        setError(QLatin1String("setProperty: Property needs to be specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+
+
+    //
+    // Convert all values to RDF nodes. This includes the property range check and conversion of local file paths to file URLs
+    //
+    const QSet<Soprano::Node> nodes = d->m_classAndPropertyTree->variantListToNodeSet(values, property);
+    if(nodes.isEmpty()) {
+        setError(d->m_classAndPropertyTree->lastError());
+        return;
+    }
+
+
+    //
+    // We need to ensure that no client removes any ontology constructs or graphs,
+    // we can check this before resolving file URLs since no protected resource will
+    // ever have a nie:url
+    //
+    if(containsResourceWithProtectedType(QSet<QUrl>::fromList(resources))) {
+        return;
+    }
+
+
+    clearError();
+
+
+    //
+    // Hash to keep mapping from provided URL/URI to resource URIs
+    //
+    QHash<Soprano::Node, Soprano::Node> resolvedNodes;
+
+    //
+    // Setting nie:url on file resources also means updating nfo:fileName and possibly nie:isPartOf
+    //
+    if(property == NIE::url()) {
+        if(resources.count() != 1) {
+            setError(QLatin1String("setProperty: no two resources can have the same nie:url."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+        else if(nodes.count() > 1) {
+            setError(QLatin1String("setProperty: One resource can only have one nie:url."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+
+        // check if another resource already uses the URL - no two resources can have the same URL at the same time
+        // CAUTION: There is one theoretical situation in which this breaks (more than this actually):
+        //          A file is moved and before the nie:url is updated data is added to the file in the new location.
+        //          At this point the file is there twice and the data should ideally be merged. But how to decide that
+        //          and how to distiguish between that situation and a file overwrite?
+        if(containsAnyStatement(Soprano::Node(), NIE::url(), *nodes.constBegin())) {
+            setError(QLatin1String("setProperty: No two resources can have the same nie:url at the same time."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+
+        if(updateNieUrlOnLocalFile(resources.first(), nodes.constBegin()->uri())) {
+            return;
+        }
+
+        // nie:url is the only property for which we do not want to resolve URLs
+        resolvedNodes.insert(*nodes.constBegin(), *nodes.constBegin());
+    }
+    else {
+        resolvedNodes = resolveNodes(nodes);
+        if(lastError()) {
+            return;
+        }
+    }
+
+    //
+    // Resolve local file URLs
+    //
+    QHash<QUrl, QUrl> uriHash = resolveUrls(resources);
+    if(lastError()) {
+        return;
+    }
+
+    //
+    // Remove values that are not wanted anymore
+    //
+    const QStringList uriHashN3 = resourceHashToN3(uriHash);
+    if(!uriHashN3.isEmpty()) {
+        const QSet<Soprano::Node> existingValues = QSet<Soprano::Node>::fromList(resolvedNodes.values());
+        QSet<QUrl> graphs;
+        QList<Soprano::BindingSet> existing
+                = executeQuery(QString::fromLatin1("select ?r ?v ?g where { graph ?g { ?r %1 ?v . FILTER(?r in (%2)) . } . }")
+                               .arg(Soprano::Node::resourceToN3(property),
+                                    uriHashN3.join(QLatin1String(","))),
+                               Soprano::Query::QueryLanguageSparql).allBindings();
+        Q_FOREACH(const Soprano::BindingSet& binding, existing) {
+            if(!existingValues.contains(binding["v"])) {
+                removeAllStatements(binding["r"], property, binding["v"]);
+                graphs.insert(binding["g"].uri());
+                d->m_watchManager->removeProperty(binding["r"], property, binding["v"]);
+            }
+        }
+        removeTrailingGraphs(graphs);
+    }
+
+
+    //
+    // And finally add the rest of the statements (only if there is anything to add)
+    //
+    if(!nodes.isEmpty()) {
+        addProperty(uriHash, property, resolvedNodes, app);
+    }
+}
+
+void Nepomuk::DataManagementModel::removeProperty(const QList<QUrl> &resources, const QUrl &property, const QVariantList &values, const QString &app)
+{
+    // 1. remove the triples
+    // 2. remove trailing graphs
+    // 3. update resource mtime only if we actually change anything on the resource
+
+    //
+    // Check arguments
+    //
+    if(app.isEmpty()) {
+        setError(QLatin1String("removeProperty: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(resources.isEmpty()) {
+        setError(QLatin1String("removeProperty: No resource specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    foreach( const QUrl & res, resources ) {
+        if(res.isEmpty()) {
+            setError(QLatin1String("removeProperty: Encountered empty resource URI."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+    if(property.isEmpty()) {
+        setError(QLatin1String("removeProperty: Property needs to be specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(values.isEmpty()) {
+        setError(QLatin1String("removeProperty: Values needs to be specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(d->m_protectedProperties.contains(property)) {
+        setError(QString::fromLatin1("removeProperty: %1 is a protected property which can only be changed by the data management service itself.").arg(property.toString()),
+                 Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+
+    const QSet<Soprano::Node> valueNodes = d->m_classAndPropertyTree->variantListToNodeSet(values, property);
+    if(valueNodes.isEmpty()) {
+        setError(d->m_classAndPropertyTree->lastError());
+        return;
+    }
+
+
+    clearError();
+
+
+    //
+    // Resolve file URLs, we can simply ignore the non-existing file resources which are reflected by empty resolved URIs
+    //
+    QSet<QUrl> resolvedResources = QSet<QUrl>::fromList(resolveUrls(resources).values());
+    resolvedResources.remove(QUrl());
+    if(resolvedResources.isEmpty() || lastError()) {
+        return;
+    }
+
+    QSet<Soprano::Node> resolvedNodes = QSet<Soprano::Node>::fromList(resolveNodes(valueNodes).values());
+    resolvedNodes.remove(Soprano::Node());
+    if(resolvedNodes.isEmpty() || lastError()) {
+        return;
+    }
+
+
+    //
+    // We need to ensure that no client removes any ontology constructs or graphs
+    //
+    if(containsResourceWithProtectedType(resolvedResources)) {
+        return;
+    }
+
+
+    //
+    // Actually change data
+    //
+    QUrl mtimeGraph;
+    QSet<QUrl> graphs;
+    const QString propertyN3 = Soprano::Node::resourceToN3(property);
+    foreach( const QUrl & res, resolvedResources ) {
+        const QList<Soprano::BindingSet> valueGraphs
+                = executeQuery(QString::fromLatin1("select ?g ?v where { graph ?g { %1 %2 ?v . } . FILTER(?v in (%3)) . }")
+                               .arg(Soprano::Node::resourceToN3(res),
+                                    propertyN3,
+                                    nodesToN3(resolvedNodes).join(QLatin1String(","))),
+                               Soprano::Query::QueryLanguageSparql).allBindings();
+
+        foreach(const Soprano::BindingSet& binding, valueGraphs) {
+            graphs.insert( binding["g"].uri() );
+            removeAllStatements( res, property, binding["v"] );
+            d->m_watchManager->removeProperty( res, property, binding["v"]);
+        }
+
+        // we only update the mtime in case we actually remove anything
+        if(!valueGraphs.isEmpty()) {
+            // If the resource is now empty we remove it completely
+            if(!doesResourceExist(res)) {
+                removeResources(QList<QUrl>() << res, NoRemovalFlags, app);
+            }
+            else {
+                if(mtimeGraph.isEmpty()) {
+                    mtimeGraph = createGraph(app);
+                }
+                updateModificationDate(res, mtimeGraph);
+            }
+        }
+    }
+
+    removeTrailingGraphs( graphs );
+}
+
+void Nepomuk::DataManagementModel::removeProperties(const QList<QUrl> &resources, const QList<QUrl> &properties, const QString &app)
+{
+    // 1. remove the triples
+    // 2. remove trailing graphs
+    // 3. update resource mtime
+
+    //
+    // Check arguments
+    //
+    if(app.isEmpty()) {
+        setError(QLatin1String("removeProperties: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(resources.isEmpty()) {
+        setError(QLatin1String("removeProperties: No resource specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    foreach( const QUrl & res, resources ) {
+        if(res.isEmpty()) {
+            setError(QLatin1String("removeProperties: Encountered empty resource URI."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+    if(properties.isEmpty()) {
+        setError(QLatin1String("removeProperties: No properties specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    foreach(const QUrl& property, properties) {
+        if(property.isEmpty()) {
+            setError(QLatin1String("removeProperties: Encountered empty property URI."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+        else if(d->m_protectedProperties.contains(property)) {
+            setError(QString::fromLatin1("removeProperties: %1 is a protected property which can only be changed by the data management service itself.").arg(property.toString()),
+                     Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+
+
+    clearError();
+
+
+    //
+    // Resolve file URLs, we can simply ignore the non-existing file resources which are reflected by empty resolved URIs
+    //
+    QSet<QUrl> resolvedResources = QSet<QUrl>::fromList(resolveUrls(resources).values());
+    resolvedResources.remove(QUrl());
+    if(resolvedResources.isEmpty() || lastError()) {
+        return;
+    }
+
+
+    //
+    // We need to ensure that no client removes any ontology constructs or graphs
+    //
+    if(containsResourceWithProtectedType(resolvedResources)) {
+        return;
+    }
+
+
+    //
+    // Actually change data
+    //
+    QUrl mtimeGraph;
+    QSet<QUrl> graphs;
+    foreach( const QUrl & res, resolvedResources ) {
+        QSet<Soprano::Node> propertiesToRemove;
+        QList<QPair<Soprano::Node, Soprano::Node> > propertyValues;
+        Soprano::QueryResultIterator it
+                = executeQuery(QString::fromLatin1("select distinct ?g ?p ?v where { graph ?g { %1 ?p ?v . } . FILTER(?p in (%2)) . }")
+                               .arg(Soprano::Node::resourceToN3(res),
+                                    resourcesToN3(properties).join(QLatin1String(","))),
+                               Soprano::Query::QueryLanguageSparql);
+        while(it.next()) {
+            graphs.insert(it["g"].uri());
+            propertiesToRemove.insert(it["p"]);
+            propertyValues.append(qMakePair(it["p"], it["v"]));
+        }
+
+        // remove the data
+        foreach(const Soprano::Node& property, propertiesToRemove) {
+            removeAllStatements( res, property, Soprano::Node() );
+        }
+
+        // inform interested parties
+        for(QList<QPair<Soprano::Node, Soprano::Node> >::const_iterator it = propertyValues.constBegin();
+            it != propertyValues.constEnd(); ++it) {
+            d->m_watchManager->removeProperty(res, it->first.uri(), it->second);
+        }
+
+        // we only update the mtime in case we actually remove anything
+        if(!propertiesToRemove.isEmpty()) {
+            // If the resource is now empty we remove it completely
+            if(!doesResourceExist(res)) {
+                removeResources(QList<QUrl>() << res, NoRemovalFlags, app);
+            }
+            else {
+                if(mtimeGraph.isEmpty()) {
+                    mtimeGraph = createGraph(app);
+                }
+                updateModificationDate(res, mtimeGraph);
+            }
+        }
+    }
+
+    removeTrailingGraphs( graphs );
+}
+
+
+QUrl Nepomuk::DataManagementModel::createResource(const QList<QUrl> &types, const QString &label, const QString &description, const QString &app)
+{
+    // 1. create an new graph
+    // 2. check if the app exists, if not create it in the new graph
+    // 3. create the new resource in the new graph
+    // 4. return the resource's URI
+
+    //
+    // Check parameters
+    //
+    if(app.isEmpty()) {
+        setError(QLatin1String("createResource: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return QUrl();
+    }
+    else if(types.isEmpty()) {
+        setError(QLatin1String("createResource: No type specified. Cannot create resources without a type."), Soprano::Error::ErrorInvalidArgument);
+        return QUrl();
+    }
+    else {
+        foreach(const QUrl& type, types) {
+            if(type.isEmpty()) {
+                setError(QLatin1String("createResource: Encountered empty type URI."), Soprano::Error::ErrorInvalidArgument);
+                return QUrl();
+            }
+            else if(!d->m_classAndPropertyTree->isKnownClass(type)) {
+                setError(QLatin1String("createResource: Encountered invalid type URI."), Soprano::Error::ErrorInvalidArgument);
+                return QUrl();
+            }
+        }
+    }
+
+    clearError();
+
+    // create new URIs and a new graph
+    const QUrl graph = createGraph(app);
+    const QUrl resUri = createUri(ResourceUri);
+
+    // add provided metadata
+    foreach(const QUrl& type, types) {
+        addStatement(resUri, RDF::type(), type, graph);
+    }
+    if(!label.isEmpty()) {
+        addStatement(resUri, NAO::prefLabel(), Soprano::LiteralValue::createPlainLiteral(label), graph);
+    }
+    if(!description.isEmpty()) {
+        addStatement(resUri, NAO::description(), Soprano::LiteralValue::createPlainLiteral(description), graph);
+    }
+
+    // add basic metadata to the new resource
+    const QDateTime now = QDateTime::currentDateTime();
+    addStatement(resUri, NAO::created(), Soprano::LiteralValue(now), graph);
+    addStatement(resUri, NAO::lastModified(), Soprano::LiteralValue(now), graph);
+
+    // inform interested parties
+    d->m_watchManager->createResource(resUri, types);
+
+    return resUri;
+}
+
+void Nepomuk::DataManagementModel::removeResources(const QList<QUrl> &resources, RemovalFlags flags, const QString &app)
+{
+    kDebug() << resources << app << flags;
+
+    Q_UNUSED(app);
+    // 1. get all sub-resources and check if they are used by some other resource (not in the list of resources to remove)
+    //    for the latter one can use a bif:exists and a !filter(?s in <s>, <s>, ...) - based on the value of force
+    // 2. remove the resources and the sub-resources
+    // 3. drop trailing graphs (could be optimized by enumerating the graphs up front)
+
+    //
+    // Check parameters
+    //
+    if(app.isEmpty()) {
+        setError(QLatin1String("removeResources: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(resources.isEmpty()) {
+        setError(QLatin1String("removeResources: No resource specified."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    foreach( const QUrl & res, resources ) {
+        if(res.isEmpty()) {
+            setError(QLatin1String("removeResources: Encountered empty resource URI."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+
+
+    //
+    // Resolve file URLs, we can simply ignore the non-existing file resources which are reflected by empty resolved URIs
+    //
+    QSet<QUrl> resolvedResources = QSet<QUrl>::fromList(resolveUrls(resources).values());
+    resolvedResources.remove(QUrl());
+    if(resolvedResources.isEmpty() || lastError()) {
+        return;
+    }
+
+
+    //
+    // We need to ensure that no client removes any ontology constructs or graphs
+    //
+    if(containsResourceWithProtectedType(resolvedResources)) {
+        return;
+    }
+
+
+    clearError();
+
+
+    //
+    // Actually remove the data
+    //
+    removeAllResources(resolvedResources, flags);
+}
+
+void Nepomuk::DataManagementModel::removeDataByApplication(const QList<QUrl> &resources, RemovalFlags flags, const QString &app)
+{
+    //
+    // Check parameters
+    //
+    if(app.isEmpty()) {
+        setError(QLatin1String("removeDataByApplication: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    foreach( const QUrl & res, resources ) {
+        if(res.isEmpty()) {
+            setError(QLatin1String("removeDataByApplication: Encountered empty resource URI."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+
+
+    clearError();
+
+
+    // we handle one special case below: legacy data from the file indexer
+    const QUrl appRes = findApplicationResource(app, false);
+    if(appRes.isEmpty() && app != QLatin1String("nepomukindexer")) {
+        return;
+    }
+
+
+    //
+    // Resolve file URLs, we can simply ignore the non-existing file resources which are reflected by empty resolved URIs
+    //
+    QSet<QUrl> resolvedResources = QSet<QUrl>::fromList(resolveUrls(resources).values());
+    resolvedResources.remove(QUrl());
+    if(resolvedResources.isEmpty() || lastError()) {
+        return;
+    }
+
+
+    //
+    // Handle the sub-resources: we can delete all sub-resources of the deleted ones that are entirely defined by our app
+    // and are not related by other resources.
+    // this has to be done before deleting the resouces in resolvedResources. Otherwise the nao:hasSubResource relationships are already gone!
+    //
+    // Explanation of the query:
+    // The query selects all subresources of the resources in resolvedResources.
+    // It then filters out the sub-resources that have properties defined by other apps which are not metadata.
+    // It then filters out the sub-resources that are related from other resources that are not the ones being deleted.
+    //
+    if(flags & RemoveSubResoures && !appRes.isEmpty()) {
+        QSet<QUrl> subResources = resolvedResources;
+        int resCount = 0;
+        do {
+            resCount = resolvedResources.count();
+            Soprano::QueryResultIterator it
+                    = executeQuery(QString::fromLatin1("select ?r where { graph ?g { ?r ?p ?o . } . "
+                                                       "?parent %1 ?r . "
+                                                       "FILTER(?parent in (%2)) . "
+                                                       "?g %3 %4 . "
+                                                       "FILTER(!bif:exists((select (1) where { graph ?g2 { ?r ?p2 ?o2 . } . ?g2 %3 ?a2 . FILTER(?a2!=%4) . FILTER(%6) . }))) . "
+                                                       "FILTER(!bif:exists((select (1) where { graph ?g2 { ?r2 ?p3 ?r . FILTER(%5) . } . FILTER(!bif:exists((select (1) where { ?x %1 ?r2 . FILTER(?x in (%2)) . }))) . }))) . "
+                                                       "}")
+                                   .arg(Soprano::Node::resourceToN3(NAO::hasSubResource()),
+                                        resourcesToN3(subResources).join(QLatin1String(",")),
+                                        Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                        Soprano::Node::resourceToN3(appRes),
+                                        createResourceFilter(subResources, QLatin1String("?r2")),
+                                        createResourceMetadataPropertyFilter(QLatin1String("?p2"))),
+                                   Soprano::Query::QueryLanguageSparql);
+            subResources.clear();
+            while(it.next()) {
+                subResources << it[0].uri();
+            }
+            resolvedResources += subResources;
+        } while(resCount < resolvedResources.count());
+    }
+
+    QList<Soprano::BindingSet> graphRemovalCandidates;
+
+    if(!appRes.isEmpty()) {
+        //
+        // Get the graphs we need to check with removeTrailingGraphs later on.
+        // query all graphs the app maintains which contain any information about one of the resources
+        // count the number of apps maintaining those graphs (later we will only remove the app as maintainer but keep the graph)
+        // count the number of metadata properties defined in those graphs (we will keep that information in case the resource is not fully removed)
+        //
+        // We combine the results of 2 queries since Virtuoso can optimize FILTER(?r in (...)) but cannot optimize FILTER(?r in (...) || ?o in (...))
+        //
+        graphRemovalCandidates += executeQuery(QString::fromLatin1("select distinct "
+                                                                   "?g "
+                                                                   "(select count(distinct ?app) where { ?g %1 ?app . }) as ?c "
+                                                                   "where { "
+                                                                   "graph ?g { ?r ?p ?o . FILTER(?r in (%3)) . } . "
+                                                                   "?g %1 %2 . }")
+                                               .arg(Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                                    Soprano::Node::resourceToN3(appRes),
+                                                    resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                                               Soprano::Query::QueryLanguageSparql).allElements();
+        graphRemovalCandidates += executeQuery(QString::fromLatin1("select distinct "
+                                                                   "?g "
+                                                                   "(select count(distinct ?app) where { ?g %1 ?app . }) as ?c "
+                                                                   "where { "
+                                                                   "graph ?g { ?r ?p ?o . FILTER(?o in (%3)) . } . "
+                                                                   "?g %1 %2 . }")
+                                               .arg(Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                                    Soprano::Node::resourceToN3(appRes),
+                                                    resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                                               Soprano::Query::QueryLanguageSparql).allElements();
+    }
+
+
+    //
+    // Handle legacy file indexer data which was stored in graphs marked via a special property
+    //
+    if(app == QLatin1String("nepomukindexer")) {
+        graphRemovalCandidates += executeQuery(QString::fromLatin1("select distinct ?g (0) as ?c where { "
+                                                                   "?g <"STRIGI_INDEX_GRAPH_FOR"> ?r . "
+                                                                   "FILTER(?r in (%1)) . }")
+                                               .arg(Nepomuk::resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                                               Soprano::Query::QueryLanguageSparql).allElements();
+    }
+
+    //
+    // Split the list of graph removal candidates into those we actually remove and those we only stop maintaining
+    //
+    QSet<QUrl> graphsToRemove;
+    QSet<QUrl> graphsToStopMaintaining;
+    for(QList<Soprano::BindingSet>::const_iterator it = graphRemovalCandidates.constBegin(); it != graphRemovalCandidates.constEnd(); ++it) {
+        if(it->value("c").literal().toInt() <= 1) {
+            graphsToRemove << it->value("g").uri();
+        }
+        else {
+            graphsToStopMaintaining << it->value("g").uri();
+        }
+    }
+    // free some mem
+    graphRemovalCandidates.clear();
+
+
+    // the set of resources that we did modify but not remove entirely
+    QSet<QUrl> modifiedResources;
+
+
+    //
+    // Fetch all resources that are changed, ie. that are related to the deleted resource in some way.
+    // We need to update the mtime of those resources, too.
+    //
+    if(!appRes.isEmpty()) {
+        Soprano::QueryResultIterator relatedResIt = executeQuery(QString::fromLatin1("select distinct ?r where { "
+                                                                                     "graph ?g { ?r ?p ?rr . } . "
+                                                                                     "?g %1 %2 . "
+                                                                                     "FILTER(?rr in (%3)) . "
+                                                                                     "FILTER(!(?r in (%3))) . }")
+                                                                 .arg(Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                                                      Soprano::Node::resourceToN3(appRes),
+                                                                      resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                                                                 Soprano::Query::QueryLanguageSparql);
+        while(relatedResIt.next()) {
+            modifiedResources.insert(relatedResIt[0].uri());
+        }
+    }
+
+
+    //
+    // remove the resources
+    // Other apps might be maintainer, too. In that case only remove the app as a maintainer but keep the data
+    //
+
+    //
+    // We cannot remove the metadata if the resource is not removed completely.
+    // Thus, we cache it and deal with it later. But we only need to cache the metadata from graphs
+    // we will actually delete.
+    //
+    // Tests showed that one query per graph is faster than one query for all graphs!
+    //
+    Soprano::Graph metadata;
+    foreach(const QUrl& g, graphsToRemove) {
+        Soprano::QueryResultIterator mdIt
+                = executeQuery(QString::fromLatin1("select ?r ?p ?o where { graph %3 { ?r ?p ?o . FILTER(?r in (%1)) . FILTER(%2) . } . }")
+                               .arg(resourcesToN3(resolvedResources).join(QLatin1String(",")),
+                                    createResourceMetadataPropertyFilter(QLatin1String("?p"), false),
+                                    Soprano::Node::resourceToN3(g)),
+                               Soprano::Query::QueryLanguageSparql);
+
+        while(mdIt.next()) {
+            metadata.addStatement(mdIt["r"], mdIt["p"], mdIt["o"]);
+        }
+    }
+
+
+    //
+    // Gather the resources that we actually change, ie. those which have non-metadata props in the removed graphs
+    //
+    Soprano::QueryResultIterator mResIt
+            = executeQuery(QString::fromLatin1("select ?r where { graph ?g { ?r ?p ?o . FILTER(?r in (%1)) . FILTER(%2) . } . FILTER(?g in (%3)) . }")
+                                        .arg(resourcesToN3(resolvedResources).join(QLatin1String(",")),
+                                             createResourceMetadataPropertyFilter(QLatin1String("?p"), true),
+                                             resourcesToN3(graphsToRemove).join(QLatin1String(","))),
+                                        Soprano::Query::QueryLanguageSparql);
+    while(mResIt.next()) {
+        modifiedResources.insert(mResIt[0].uri());
+    }
+
+
+    //
+    // Remove the actual data. This has to be done using removeAllStatements. Otherwise the crappy inferencer cannot follow the changes.
+    //
+    foreach(const QUrl& g, graphsToRemove) {
+        foreach(const QUrl& res, resolvedResources) {
+            removeAllStatements(res, Soprano::Node(), Soprano::Node(), g);
+            removeAllStatements(Soprano::Node(), Soprano::Node(), res, g);
+        }
+    }
+
+
+    //
+    // Remove the app as mainainer from the rest of the graphs
+    //
+    foreach(const QUrl& g, graphsToStopMaintaining) {
+        const int otherCnt = executeQuery(QString::fromLatin1("select count (distinct ?other) where { "
+                                                              "graph %1 { ?other ?op ?oo . "
+                                                              "FILTER(!(?other in (%2)) && !(?oo in (%2))) . "
+                                                              "} . }")
+                                          .arg(Soprano::Node::resourceToN3(g),
+                                               resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                                          Soprano::Query::QueryLanguageSparql).iterateBindings(0).allElements().first().literal().toInt();
+        if(otherCnt > 0) {
+            //
+            // if the graph contains anything else besides the data we want to delete
+            // we need to keep the app as maintainer of that data. That is only possible
+            // by splitting the graph.
+            //
+            const QUrl newGraph = splitGraph(g, QUrl(), QUrl());
+            Q_ASSERT(!newGraph.isEmpty());
+
+            //
+            // we now have two graphs the the same metadata: g and newGraph.
+            // we now move the resources we delete into the new graph and only
+            // remove the app as maintainer from that graph.
+            // (we can use fancy queries since we do not actually change data. Thus
+            // the crappy inferencer and other models which act on statement commands
+            // are not really interested)
+            //
+            executeQuery(QString::fromLatin1("insert into %1 { ?r ?p ?o . } where { graph %2 { ?r ?p ?o . FILTER(?r in (%3) || ?o in (%3)) . } . }")
+                         .arg(Soprano::Node::resourceToN3(newGraph),
+                              Soprano::Node::resourceToN3(g),
+                              resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                         Soprano::Query::QueryLanguageSparql);
+            executeQuery(QString::fromLatin1("delete from %1 { ?r ?p ?o . } where { ?r ?p ?o . FILTER(?r in (%2) || ?o in (%2)) . }")
+                         .arg(Soprano::Node::resourceToN3(g),
+                              resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                         Soprano::Query::QueryLanguageSparql);
+
+            // and finally remove the app as maintainer of the new graph
+            removeAllStatements(newGraph, NAO::maintainedBy(), appRes);
+        }
+        else {
+            // we simply remove the app as maintainer of this graph
+            removeAllStatements(g, NAO::maintainedBy(), appRes);
+        }
+    }
+
+
+    //
+    // Determine the resources we did not remove completely.
+    // This includes resource that have still other properties than the metadata ones defined
+    // and those that have relations from other resources created by other applications and have a nie:url.
+    // The latter is a special case which has two reasons:
+    // 1. A nie:url identifies the resource even outside of Nepomuk
+    // 2. The indexers use this method to update indexed data. If the resource has incoming relations
+    //    they should be kept between updates. (The only exception is the legacy index graph relation.)
+    //
+    QSet<QUrl> resourcesToRemoveCompletely(resolvedResources);
+    Soprano::QueryResultIterator resComplIt
+            = executeQuery(QString::fromLatin1("select ?r where { ?r ?p ?o . FILTER(?r in (%1)) . FILTER(%2) . }")
+                           .arg(resourcesToN3(resolvedResources).join(QLatin1String(",")),
+                                createResourceMetadataPropertyFilter(QLatin1String("?p"))),
+                           Soprano::Query::QueryLanguageSparql);
+    while(resComplIt.next()) {
+        resourcesToRemoveCompletely.remove(resComplIt[0].uri());
+    }
+    resComplIt = executeQuery(QString::fromLatin1("select ?r where { ?o ?p ?r . FILTER(?r in (%1)) . FILTER(?p != <"STRIGI_INDEX_GRAPH_FOR">) . }")
+                              .arg(resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                              Soprano::Query::QueryLanguageSparql);
+    while(resComplIt.next()) {
+        const QUrl r = resComplIt[0].uri();
+        if(metadata.containsAnyStatement(r, NIE::url(), Soprano::Node())) {
+            resourcesToRemoveCompletely.remove(resComplIt[0].uri());
+        }
+    }
+
+    // no need to update metadata on resource we remove completely
+    modifiedResources -= resourcesToRemoveCompletely;
+
+
+    //
+    // Re-add the metadata for the resources that we did not remove entirely
+    //
+    // 1. use the same time for all mtime changes
+    const QDateTime now = QDateTime::currentDateTime();
+    // 2. Remove the metadata for the resources we remove completely
+    foreach(const QUrl& res, resourcesToRemoveCompletely) {
+        metadata.removeAllStatements(res, Soprano::Node(), Soprano::Node());
+    }
+    // 3. Update the mtime for modifiedResources as those are the only ones we did touch
+    foreach(const QUrl& res, modifiedResources) {
+        if(metadata.containsAnyStatement(res, NAO::lastModified(), Soprano::Node())) {
+            metadata.removeAllStatements(res, NAO::lastModified(), Soprano::Node());
+            metadata.addStatement(res, NAO::lastModified(), Soprano::LiteralValue(now));
+            modifiedResources.remove(res);
+        }
+    }
+    // 4. add all the updated metadata into a new metadata graph
+    if(!metadata.isEmpty() || !modifiedResources.isEmpty()) {
+        const QUrl metadataGraph = createGraph();
+        if(lastError()) {
+            return;
+        }
+
+        foreach(const Soprano::Statement& s, metadata.toList()) {
+            addStatement(s.subject(), s.predicate(), s.object(), metadataGraph);
+        }
+
+        // update mtime of all resources we did touch and not handle above yet
+        updateModificationDate(modifiedResources, metadataGraph, now);
+    }
+
+
+    //
+    // Remove the resources that are gone completely
+    //
+    if(!resourcesToRemoveCompletely.isEmpty()){
+        removeAllResources(resourcesToRemoveCompletely, flags);
+    }
+
+    // make sure we do not leave trailing graphs behind
+    removeTrailingGraphs(graphsToRemove);
+}
+
+namespace {
+struct GraphRemovalCandidate {
+    int appCnt;
+    QHash<QUrl, int> resources;
+};
+}
+
+void Nepomuk::DataManagementModel::removeDataByApplication(RemovalFlags flags, const QString &app)
+{
+    //
+    // Check parameters
+    //
+    if(app.isEmpty()) {
+        setError(QLatin1String("removeDataByApplication: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+
+    clearError();
+
+    const QUrl appRes = findApplicationResource(app, false);
+    if(appRes.isEmpty()) {
+        return;
+    }
+
+
+    //
+    // Get the graphs we need to check with removeTrailingGraphs later on.
+    // query all graphs the app maintains
+    // count the number of apps maintaining those graphs (later we will only remove the app as maintainer but keep the graph)
+    // count the number of metadata properties defined in those graphs (we will keep that information in case the resource is not fully removed)
+    //
+    QHash<QUrl, GraphRemovalCandidate> graphRemovalCandidates;
+    Soprano::QueryResultIterator it
+            = executeQuery(QString::fromLatin1("select distinct "
+                                               "?g ?r "
+                                               "(select count(distinct ?app) where { ?g %1 ?app . }) as ?c "
+                                               "(select count (*) where { graph ?g { ?r ?mp ?mo . FILTER(%3) . } . }) as ?mc "
+                                               "where { "
+                                               "graph ?g { ?r ?p ?o . } . "
+                                               "?g %1 %2 . "
+                                               "}")
+                           .arg(Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                Soprano::Node::resourceToN3(appRes),
+                                createResourceMetadataPropertyFilter(QLatin1String("?mp"), false)),
+                           Soprano::Query::QueryLanguageSparql);
+    while(it.next()) {
+        GraphRemovalCandidate& g = graphRemovalCandidates[it["g"].uri()];
+        g.appCnt = it["c"].literal().toInt();
+        g.resources[it["r"].uri()] = it["mc"].literal().toInt();
+    }
+
+    QSet<QUrl> allResources;
+    QSet<QUrl> graphs;
+    const QDateTime now = QDateTime::currentDateTime();
+    QUrl mtimeGraph;
+    for(QHash<QUrl, GraphRemovalCandidate>::const_iterator it = graphRemovalCandidates.constBegin(); it != graphRemovalCandidates.constEnd(); ++it) {
+        const QUrl& g = it.key();
+        const int appCnt = it.value().appCnt;
+        if(appCnt == 1) {
+            //
+            // Run through all resources and see what needs to be done
+            // TODO: this can surely be improved by moving at least one query
+            // one layer above
+            //
+            for(QHash<QUrl, int>::const_iterator resIt = it->resources.constBegin(); resIt != it->resources.constEnd(); ++resIt) {
+                const QUrl& res = resIt.key();
+                const int metadataPropCount = resIt.value();
+                if(doesResourceExist(res, g)) {
+                    //
+                    // We cannot remove the metadata if the resource is not removed completely
+                    // Thus, we re-add them later on.
+                    // trueg: as soon as we have real inference and do not rely on the crappy inferencer to update types via the
+                    //        Soprano statement manipulation methods we can use powerful queries like this one:
+                    //                    executeQuery(QString::fromLatin1("delete from %1 { %2 ?p ?o . } where { %2 ?p ?o . FILTER(%3) . }")
+                    //                                 .arg(Soprano::Node::resourceToN3(g),
+                    //                                      Soprano::Node::resourceToN3(res),
+                    //                                      createResourceMetadataPropertyFilter(QLatin1String("?p"))),
+                    //                                 Soprano::Query::QueryLanguageSparql);
+                    //
+                    QList<Soprano::BindingSet> metadataProps;
+                    if(metadataPropCount > 0) {
+                        // remember the metadata props
+                        metadataProps = executeQuery(QString::fromLatin1("select ?p ?o where { graph %1 { %2 ?p ?o . FILTER(%3) . } . }")
+                                                     .arg(Soprano::Node::resourceToN3(g),
+                                                          Soprano::Node::resourceToN3(res),
+                                                          createResourceMetadataPropertyFilter(QLatin1String("?p"), false)),
+                                                     Soprano::Query::QueryLanguageSparql).allBindings();
+                    }
+
+                    removeAllStatements(res, Soprano::Node(), Soprano::Node(), g);
+                    removeAllStatements(Soprano::Node(), Soprano::Node(), res, g);
+
+                    foreach(const Soprano::BindingSet& set, metadataProps)  {
+                        addStatement(res, set["p"], set["o"], g);
+                    }
+
+                    // update mtime
+                    if(mtimeGraph.isEmpty()) {
+                        mtimeGraph = createGraph(app);
+                        if(lastError()) {
+                            return;
+                        }
+                    }
+                    updateModificationDate(res, mtimeGraph, now);
+                }
+
+                allResources.insert(res);
+            }
+
+
+            graphs.insert(g);
+        }
+        else {
+            // we simply remove the app as maintainer of this graph
+            removeAllStatements(g, NAO::maintainedBy(), appRes);
+        }
+    }
+
+
+    // make sure we do not leave anything empty trailing around and propery update the mtime
+    QList<QUrl> resourcesToRemoveCompletely;
+    foreach(const QUrl& res, allResources) {
+        if(!doesResourceExist(res)) {
+            resourcesToRemoveCompletely << res;
+        }
+    }
+    if(!resourcesToRemoveCompletely.isEmpty()){
+        removeResources(resourcesToRemoveCompletely, flags, app);
+    }
+
+
+    removeTrailingGraphs(graphs);
+}
+
+
+//// TODO: do not allow to create properties or classes this way
+void Nepomuk::DataManagementModel::storeResources(const Nepomuk::SimpleResourceGraph &resources,
+                                                  const QString &app,
+                                                  Nepomuk::StoreIdentificationMode identificationMode,
+                                                  Nepomuk::StoreResourcesFlags flags,
+                                                  const QHash<QUrl, QVariant> &additionalMetadata)
+{
+    if(app.isEmpty()) {
+        setError(QLatin1String("storeResources: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(resources.isEmpty()) {
+        clearError();
+        return;
+    }
+
+    /// Holds the mapping of <file://url> to <nepomuk:/res/> uris
+    QHash<QUrl, QUrl> resolvedNodes;
+
+    //
+    // Resolve the nie URLs which are present as resource uris
+    //
+    QSet<QUrl> allNonFileResources;
+    SimpleResourceGraph resGraph( resources );
+    QList<SimpleResource> resGraphList = resGraph.toList();
+    QMutableListIterator<SimpleResource> iter( resGraphList );
+    while( iter.hasNext() ) {
+        SimpleResource & res = iter.next();
+
+        if( !res.isValid() ) {
+            setError(QLatin1String("storeResources: One of the resources is Invalid."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+
+        const UriState state = uriState(res.uri());
+        if(state == NepomukUri || state == BlankUri) {
+            allNonFileResources << res.uri();
+        }
+        // Handle nie urls
+        else if(state == NonExistingFileUrl) {
+            setError(QString::fromLatin1("Cannot store information about non-existing local files. File '%1' does not exist.").arg(res.uri().toLocalFile()), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+        else if(state == ExistingFileUrl || state == SupportedUrl) {
+            const QUrl nieUrl = res.uri();
+            QUrl newResUri = resolveUrl( nieUrl );
+            if( lastError() )
+                return;
+
+            if( newResUri.isEmpty() ) {
+                // Resolution of one url failed. Assign it a random blank uri
+                newResUri = SimpleResource().uri(); // HACK: improveme
+
+                res.addProperty( NIE::url(), nieUrl );
+                res.addProperty( RDF::type(), NFO::FileDataObject() );
+                if( QFileInfo( nieUrl.toLocalFile() ).isDir() )
+                    res.addProperty( RDF::type(), NFO::Folder() );
+            }
+            resolvedNodes.insert( nieUrl, newResUri );
+
+            res.setUri( newResUri );
+        }
+        else if( state == OtherUri ) {
+            // Legacy support - Sucks but we need it
+            const QUrl legacyUri = resolveUrl( res.uri() );
+            if( lastError() )
+                return;
+
+            allNonFileResources << legacyUri;
+        }
+        else if( state == OntologyUri ) {
+            setError(QLatin1String("It is not allowed to add classes or properties through this API."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+    resGraph = resGraphList;
+
+
+    //
+    // We need to ensure that no client removes any ontology constructs or graphs
+    //
+    if(!allNonFileResources.isEmpty() &&
+            containsResourceWithProtectedType(allNonFileResources)) {
+        return;
+    }
+
+
+    ResourceIdentifier resIdent( identificationMode, this );
+    QList<Soprano::Statement> allStatements;
+    QList<Sync::SyncResource> extraResources;
+
+
+    //
+    // Resolve URLs in property values and prepare the resource identifier
+    //
+    foreach( const SimpleResource& res, resGraph.toList() ) {
+        // Convert to a Sync::SyncResource
+        //
+        Sync::SyncResource syncRes( res.uri() ); //vHanda: Will this set the uri properly?
+        QHashIterator<QUrl, QVariant> hit( res.properties() );
+        while( hit.hasNext() ) {
+            hit.next();
+
+            Soprano::Node n = d->m_classAndPropertyTree->variantToNode( hit.value(), hit.key() );
+            // The ClassAndPropertyTree returns blank nodes as URIs. It does not understand the
+            // concept of blank nodes, and since only storeResources requires blank nodes,
+            // it's easier to keep the blank node logic outside.
+            if( n.isResource() )
+                n = convertIfBlankUri( n.uri() );
+
+            const Soprano::Error::Error error = d->m_classAndPropertyTree->lastError();
+            if( error ) {
+                setError( error.message(), error.code() );
+                return;
+            }
+            syncRes.insert( hit.key(), n );
+        }
+
+        QMutableHashIterator<KUrl, Soprano::Node> it( syncRes );
+        while( it.hasNext() ) {
+            it.next();
+
+            const Soprano::Node object = it.value();
+            if( object.isResource() && it.key() != NIE::url() ) {
+                const UriState state = uriState(object.uri());
+                if(state==NepomukUri || state==BlankUri || state == OntologyUri) {
+                    continue;
+                }
+                else if(state == NonExistingFileUrl) {
+                    setError(QString::fromLatin1("Cannot store information about non-existing local files. File '%1' does not exist.").arg(object.uri().toLocalFile()),
+                             Soprano::Error::ErrorInvalidArgument);
+                    return;
+                }
+                else if(state == ExistingFileUrl || state==SupportedUrl) {
+                    const QUrl nieUrl = object.uri();
+                    // Need to resolve it
+                    QHash< QUrl, QUrl >::const_iterator findIter = resolvedNodes.constFind( nieUrl );
+                    if( findIter != resolvedNodes.constEnd() ) {
+                        it.setValue( convertIfBlankUri(findIter.value()) );
+                    }
+                    else {
+                        Sync::SyncResource newRes;
+
+                        // It doesn't exist, create it
+                        QUrl resolvedUri = resolveUrl( nieUrl );
+                        if( resolvedUri.isEmpty() ) {
+                            resolvedUri = SimpleResource().uri(); // HACK: improveme
+
+                            newRes.insert( RDF::type(), NFO::FileDataObject() );
+                            newRes.insert( NIE::url(), nieUrl );
+                            if( QFileInfo( nieUrl.toLocalFile() ).isDir() )
+                                newRes.insert( RDF::type(), NFO::Folder() );
+                        }
+
+                        newRes.setUri( resolvedUri );
+                        extraResources.append( newRes );
+
+                        resolvedNodes.insert( nieUrl, resolvedUri );
+                        it.setValue( convertIfBlankUri(resolvedUri) );
+                    }
+                }
+                else if(state == OtherUri) {
+                    // We use resolveUrl to check if the otherUri exists. If it doesn't exist,
+                    // then resolveUrl which set the last error
+                    const QUrl legacyUri = resolveUrl( object.uri() );
+                    if( lastError() )
+                        return;
+
+                    // It apparently exists, so we must support it
+                }
+            }
+        } // while( it.hasNext() )
+
+        // The resource is now ready.
+        // Push it into the Resource Identifier
+        QList< Soprano::Statement > stList = syncRes.toStatementList();
+        allStatements << stList;
+
+        if(stList.isEmpty()) {
+            setError(d->m_classAndPropertyTree->lastError());
+            return;
+        }
+
+        if( !syncRes.isValid() ) {
+            setError(QLatin1String("storeResources: Contains invalid resources."), Soprano::Error::ErrorParsingFailed);
+            return;
+        }
+        resIdent.addSyncResource( syncRes );
+    }
+
+
+    //
+    // Check the created statements
+    //
+    foreach(const Soprano::Statement& s, allStatements) {
+        if(!s.isValid()) {
+            kDebug() << "Invalid statement after resource conversion:" << s;
+            setError(QLatin1String("storeResources: Encountered invalid statement after resource conversion."), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+
+    //
+    // For better identification add rdf:type and nie:url to resolvedNodes
+    //
+    QHashIterator<QUrl, QUrl> it( resolvedNodes );
+    while( it.hasNext() ) {
+        it.next();
+
+        const QUrl fileUrl = it.key();
+        const QUrl uri = it.value();
+
+        const Sync::SyncResource existingRes = resIdent.simpleResource( uri );
+
+        Sync::SyncResource res;
+        res.setUri( uri );
+
+        if( !existingRes.contains( RDF::type(), NFO::FileDataObject() ) )
+            res.insert( RDF::type(), NFO::FileDataObject() );
+
+        if( !existingRes.contains( NIE::url(), fileUrl ) )
+            res.insert( NIE::url(), fileUrl );
+
+        if( QFileInfo( fileUrl.toString() ).isDir() && !existingRes.contains( RDF::type(), NFO::Folder() ) )
+            res.insert( RDF::type(), NFO::Folder() );
+
+        resIdent.addSyncResource( res );
+    }
+
+    clearError();
+
+
+    //
+    // Perform the actual identification
+    //
+    resIdent.identifyAll();
+
+    if( resIdent.mappings().empty() ) {
+        kDebug() << "Nothing was mapped merging everything as it is.";
+    }
+
+    foreach( const Sync::SyncResource & res, extraResources ) {
+        allStatements << res.toStatementList();
+    }
+
+    ResourceMerger merger( this, app, additionalMetadata, flags );
+    merger.setMappings( resIdent.mappings() );
+    if( !merger.merge( Soprano::Graph(allStatements) ) ) {
+        kDebug() << " MERGING FAILED! ";
+        kDebug() << "Setting error!" << merger.lastError();
+        setError( merger.lastError() );
+    }
+}
+
+void Nepomuk::DataManagementModel::importResources(const QUrl &url,
+                                                   const QString &app,
+                                                   Soprano::RdfSerialization serialization,
+                                                   const QString &userSerialization,
+                                                   Nepomuk::StoreIdentificationMode identificationMode,
+                                                   Nepomuk::StoreResourcesFlags flags,
+                                                   const QHash<QUrl, QVariant>& additionalMetadata)
+{
+    // download the file
+    QString tmpFileName;
+    if(!KIO::NetAccess::download(url, tmpFileName, 0)) {
+        setError(QString::fromLatin1("Failed to download '%1'.").arg(url.toString()));
+        return;
+    }
+
+    // guess the serialization
+    if(serialization == Soprano::SerializationUnknown) {
+        const QString extension = KUrl(url).fileName().section('.', -1).toLower();
+        if(extension == QLatin1String("trig"))
+            serialization = Soprano::SerializationTrig;
+        else if(extension == QLatin1String("n3"))
+            serialization = Soprano::SerializationNTriples;
+        else if(extension == QLatin1String("xml"))
+            serialization = Soprano::SerializationRdfXml;
+    }
+
+    const Soprano::Parser* parser = Soprano::PluginManager::instance()->discoverParserForSerialization(serialization, userSerialization);
+    if(!parser) {
+        setError(QString::fromLatin1("Failed to create parser for serialization '%1'").arg(Soprano::serializationMimeType(serialization, userSerialization)));
+    }
+    else {
+        SimpleResourceGraph graph;
+        Soprano::StatementIterator it = parser->parseFile(tmpFileName, QUrl(), serialization, userSerialization);
+        while(it.next()) {
+            graph.addStatement(*it);
+        }
+        if(parser->lastError()) {
+            setError(parser->lastError());
+        }
+        else if(it.lastError()) {
+            setError(it.lastError());
+        }
+        else {
+            storeResources(graph, app, identificationMode, flags, additionalMetadata);
+        }
+    }
+
+    KIO::NetAccess::removeTempFile(tmpFileName);
+}
+
+void Nepomuk::DataManagementModel::mergeResources(const QUrl &res1, const QUrl &res2, const QString &app)
+{
+    if(res1.isEmpty() || res2.isEmpty()) {
+        setError(QLatin1String("mergeResources: Encountered empty resource URI."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+    if(app.isEmpty()) {
+        setError(QLatin1String("mergeResources: Empty application specified. This is not supported."), Soprano::Error::ErrorInvalidArgument);
+        return;
+    }
+
+    //
+    // We need to ensure that no client removes any ontology constructs or graphs,
+    // we can check this before resolving file URLs since no protected resource will
+    // ever have a nie:url
+    //
+    if(containsResourceWithProtectedType(QSet<QUrl>() << res1 << res2)) {
+        return;
+    }
+
+    clearError();
+
+
+    // TODO: Is it correct that all metadata stays the same?
+
+    //
+    // Copy all property values of res2 that are not also defined for res1
+    //
+    const QList<Soprano::BindingSet> res2Properties
+            = executeQuery(QString::fromLatin1("select ?g ?p ?v (select count(distinct ?v2) where { %2 ?p ?v2 . }) as ?c where { "
+                                               "graph ?g { %1 ?p ?v . } . "
+                                               "FILTER(!bif:exists((select (1) where { %2 ?p ?v . }))) . "
+                                               "}")
+                           .arg(Soprano::Node::resourceToN3(res2),
+                                Soprano::Node::resourceToN3(res1)),
+                           Soprano::Query::QueryLanguageSparql).allBindings();
+    foreach(const Soprano::BindingSet& binding, res2Properties) {
+        const QUrl& prop = binding["p"].uri();
+        // if the property has no cardinality of 1 or no value is defined yet we can simply convert the value to res1
+        if(d->m_classAndPropertyTree->maxCardinality(prop) != 1 ||
+                binding["c"].literal().toInt() == 0) {
+            addStatement(res1, prop, binding["v"], binding["g"]);
+        }
+    }
+
+
+    //
+    // Copy all backlinks from res2 that are not valid for res1
+    //
+    const QList<Soprano::BindingSet> res2Backlinks
+            = executeQuery(QString::fromLatin1("select ?g ?p ?r where { graph ?g { ?r ?p %1 . } . "
+                                               "FILTER(?r!=%2) . "
+                                               "FILTER(!bif:exists((select (1) where { ?r ?p %2 . }))) . "
+                                               "}")
+                           .arg(Soprano::Node::resourceToN3(res2),
+                                Soprano::Node::resourceToN3(res1)),
+                           Soprano::Query::QueryLanguageSparql).allBindings();
+    foreach(const Soprano::BindingSet& binding, res2Backlinks) {
+        addStatement(binding["r"], binding["p"], res1, binding["g"]);
+    }
+
+
+    //
+    // Finally delete res2 as it is now merged into res1
+    //
+    removeResources(QList<QUrl>() << res2, NoRemovalFlags, app);
+}
+
+
+namespace {
+QVariant nodeToVariant(const Soprano::Node& node) {
+    if(node.isResource())
+        return node.uri();
+    else if(node.isBlank())
+        return QUrl(QLatin1String("_:") + node.identifier());
+    else
+        return node.literal().variant();
+}
+}
+
+Nepomuk::SimpleResourceGraph Nepomuk::DataManagementModel::describeResources(const QList<QUrl> &resources, bool includeSubResources) const
+{
+    kDebug() << resources << includeSubResources;
+
+    //
+    // check parameters
+    //
+    if(resources.isEmpty()) {
+        setError(QLatin1String("describeResources: No resource specified."), Soprano::Error::ErrorInvalidArgument);
+        return SimpleResourceGraph();
+    }
+    foreach( const QUrl & res, resources ) {
+        if(res.isEmpty()) {
+            setError(QLatin1String("describeResources: Encountered empty resource URI."), Soprano::Error::ErrorInvalidArgument);
+            return SimpleResourceGraph();
+        }
+    }
+
+
+    clearError();
+
+
+    //
+    // Split into nie:urls and URIs so we can get all data in one query
+    //
+    QSet<QUrl> nieUrls;
+    QSet<QUrl> resUris;
+    foreach( const QUrl & res, resources ) {
+        const UriState state = uriState(res);
+        if(state == NonExistingFileUrl) {
+            setError(QString::fromLatin1("Cannot store information about non-existing local files. File '%1' does not exist.").arg(res.toLocalFile()),
+                     Soprano::Error::ErrorInvalidArgument);
+            return SimpleResourceGraph();
+        }
+        else if(state == ExistingFileUrl || state == SupportedUrl) {
+            nieUrls.insert(res);
+        }
+        else if(state == NepomukUri || state == OtherUri){
+            resUris.insert(res);
+        }
+        else if(state == BlankUri) {
+            setError(QString::fromLatin1("Cannot give information about blank uris. '%1' does not exist").arg(res.toString()),
+                     Soprano::Error::ErrorInvalidArgument);
+            return SimpleResourceGraph();
+        }
+    }
+
+
+    //
+    // Build the query
+    //
+    QStringList terms;
+    if(!nieUrls.isEmpty()) {
+        terms << QString::fromLatin1("?s ?p ?o . ?s %1 ?u . FILTER(?u in (%2)) . ")
+                 .arg(Soprano::Node::resourceToN3(Vocabulary::NIE::url()),
+                      resourcesToN3(nieUrls).join(QLatin1String(",")));
+        if(includeSubResources) {
+            terms << QString::fromLatin1("?s ?p ?o . ?r %1 ?s . ?r %2 ?u . FILTER(?u in (%3)) . ")
+                     .arg(Soprano::Node::resourceToN3(NAO::hasSubResource()),
+                          Soprano::Node::resourceToN3(Vocabulary::NIE::url()),
+                          resourcesToN3(nieUrls).join(QLatin1String(",")));
+        }
+    }
+    if(!resUris.isEmpty()) {
+        terms << QString::fromLatin1("?s ?p ?o . FILTER(?s in (%1)) . ")
+                 .arg(resourcesToN3(resUris).join(QLatin1String(",")));
+        if(includeSubResources) {
+            terms << QString::fromLatin1("?s ?p ?o . ?r %1 ?s . FILTER(?r in (%2)) . ")
+                     .arg(Soprano::Node::resourceToN3(NAO::hasSubResource()),
+                          resourcesToN3(resUris).join(QLatin1String(",")));
+        }
+    }
+
+    QString query = QLatin1String("select distinct ?s ?p ?o where { ");
+    if(terms.count() == 1) {
+        query += terms.first();
+    }
+    else {
+        query += QLatin1String("{ ") + terms.join(QLatin1String("} UNION { ")) + QLatin1String("}");
+    }
+    query += QLatin1String(" }");
+
+
+    //
+    // Build the graph
+    //
+    QHash<QUrl, SimpleResource> graph;
+    Soprano::QueryResultIterator it = executeQuery(query, Soprano::Query::QueryLanguageSparql);
+    while(it.next()) {
+        const QUrl r = it["s"].uri();
+        graph[r].setUri(r);
+        graph[r].addProperty(it["p"].uri(), nodeToVariant(it["o"]));
+    }
+    if(it.lastError()) {
+        setError(it.lastError());
+        return SimpleResourceGraph();
+    }
+
+    return graph.values();
+}
+
+
+void Nepomuk::DataManagementModel::removeTrailingGraphs(const QSet<QUrl>& graphs)
+{
+    kDebug() << graphs;
+
+#ifndef NDEBUG
+    if(graphs.contains(QUrl()))
+        kDebug() << "Encountered the empty graph. This most likely stems from buggy data which was generated by some older (hopefully) version of Nepomuk. Ignoring.";
+#endif
+
+    QSet<QUrl> gl(graphs);
+    gl.remove(QUrl());
+
+    if(!gl.isEmpty()) {
+        // TODO: for some weird reason the filter statement in the query below does not work. Thus, we use a count instead.
+        QList<Soprano::Node> allMetadataGraphs;
+//                = executeQuery(QString::fromLatin1("select ?mg where { ?mg %1 ?g . FILTER(?g in (%2)) . FILTER(!bif:exists((select (1) where { graph ?g { ?s ?p ?o . } . }))) . }")
+//                               .arg(Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()),
+//                                    resourcesToN3(gl).join(QLatin1String(","))),
+//                               Soprano::Query::QueryLanguageSparql).iterateBindings(0).allNodes();
+
+        Soprano::QueryResultIterator it
+                = executeQuery(QString::fromLatin1("select ?mg (select count(*) where { graph ?g { ?s ?p ?o . } . }) as ?cnt where { ?mg %1 ?g . FILTER(?g in (%2)) . }")
+                               .arg(Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()),
+                                    resourcesToN3(gl).join(QLatin1String(","))),
+                               Soprano::Query::QueryLanguageSparql);
+        while(it.next()) {
+            if(it[1].literal().toInt() == 0)
+                allMetadataGraphs << it[0];
+        }
+
+        foreach(const Soprano::Node& mg, allMetadataGraphs) {
+            executeQuery(QString::fromLatin1("clear graph %1").arg(mg.toN3()),
+                         Soprano::Query::QueryLanguageSparql);
+        }
+    }
+}
+
+
+QUrl Nepomuk::DataManagementModel::createGraph(const QString &app, const QHash<QUrl, QVariant> &additionalMetadata)
+{
+    QHash<QUrl, Soprano::Node> graphMetaData;
+
+    for(QHash<QUrl, QVariant>::const_iterator it = additionalMetadata.constBegin();
+        it != additionalMetadata.constEnd(); ++it) {
+        Soprano::Node node = d->m_classAndPropertyTree->variantToNode(it.value(), it.key());
+        if(node.isValid()) {
+            graphMetaData.insert(it.key(), node);
+        }
+        else {
+            setError(d->m_classAndPropertyTree->lastError());
+            return QUrl();
+        }
+    }
+
+    return createGraph( app, graphMetaData );
+}
+
+QUrl Nepomuk::DataManagementModel::createGraph(const QString& app, const QMultiHash< QUrl, Soprano::Node >& additionalMetadata)
+{
+    QHash<QUrl, Soprano::Node> graphMetaData = additionalMetadata;
+
+    // determine the graph type
+    bool haveGraphType = false;
+    for(QHash<QUrl, Soprano::Node>::const_iterator it = additionalMetadata.constBegin();
+        it != additionalMetadata.constEnd(); ++it) {
+        const QUrl& property = it.key();
+
+        if(property == RDF::type()) {
+            // check if it is a valid type
+            if(!it.value().isResource()) {
+                setError(QString::fromLatin1("rdf:type has resource range. '%1' does not have a resource type.").arg(it.value().toN3()), Soprano::Error::ErrorInvalidArgument);
+                return QUrl();
+            }
+            else {
+                if(d->m_classAndPropertyTree->isChildOf(it.value().uri(), NRL::Graph()))
+                    haveGraphType = true;
+            }
+        }
+
+        else if(property == NAO::created()) {
+            if(!it.value().literal().isDateTime()) {
+                setError(QString::fromLatin1("nao:created has xsd:dateTime range. '%1' is not convertable to a dateTime.").arg(it.value().toN3()), Soprano::Error::ErrorInvalidArgument);
+                return QUrl();
+            }
+        }
+
+        else {
+            // FIXME: check property, domain, and range
+            // Reuse code from ResourceMerger::checkGraphMetadata
+        }
+    }
+
+    // add missing metadata
+    if(!haveGraphType) {
+        graphMetaData.insert(RDF::type(), NRL::InstanceBase());
+    }
+    if(!graphMetaData.contains(NAO::created())) {
+        graphMetaData.insert(NAO::created(), Soprano::LiteralValue(QDateTime::currentDateTime()));
+    }
+    if(!graphMetaData.contains(NAO::maintainedBy()) && !app.isEmpty()) {
+        graphMetaData.insert(NAO::maintainedBy(), findApplicationResource(app));
+    }
+
+    const QUrl graph = createUri(GraphUri);
+    const QUrl metadatagraph = createUri(GraphUri);
+
+    // add metadata graph itself
+    addStatement(metadatagraph, NRL::coreGraphMetadataFor(), graph, metadatagraph);
+    addStatement(metadatagraph, RDF::type(), NRL::GraphMetadata(), metadatagraph);
+
+    for(QHash<QUrl, Soprano::Node>::const_iterator it = graphMetaData.constBegin();
+        it != graphMetaData.constEnd(); ++it) {
+        addStatement(graph, it.key(), it.value(), metadatagraph);
+    }
+
+    return graph;
+}
+
+
+QUrl Nepomuk::DataManagementModel::splitGraph(const QUrl &graph, const QUrl& metadataGraph_, const QUrl &appRes)
+{
+    const QUrl newGraph = createUri(GraphUri);
+    const QUrl newMetadataGraph = createUri(GraphUri);
+
+    QUrl metadataGraph(metadataGraph_);
+    if(metadataGraph.isEmpty()) {
+        Soprano::QueryResultIterator it = executeQuery(QString::fromLatin1("select ?m where { ?m %1 %2 . } LIMIT 1")
+                                                       .arg(Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()),
+                                                            Soprano::Node::resourceToN3(graph)),
+                                                       Soprano::Query::QueryLanguageSparql);
+        if(it.next()) {
+            metadataGraph = it[0].uri();
+        }
+        else {
+            kError() << "Failed to get metadata graph for" << graph;
+            return QUrl();
+        }
+    }
+
+    // add metadata graph
+    addStatement( newMetadataGraph, NRL::coreGraphMetadataFor(), newGraph, newMetadataGraph );
+    addStatement( newMetadataGraph, RDF::type(), NRL::GraphMetadata(), newMetadataGraph );
+
+    // copy the metadata from the old graph to the new one
+    executeQuery(QString::fromLatin1("insert into %1 { %2 ?p ?o . } where { graph %3 { %4 ?p ?o . } . }")
+                 .arg(Soprano::Node::resourceToN3(newMetadataGraph),
+                      Soprano::Node::resourceToN3(newGraph),
+                      Soprano::Node::resourceToN3(metadataGraph),
+                      Soprano::Node::resourceToN3(graph)),
+                 Soprano::Query::QueryLanguageSparql);
+
+    // add the new app
+    if(!appRes.isEmpty())
+        addStatement(newGraph, NAO::maintainedBy(), appRes, newMetadataGraph);
+
+    return newGraph;
+}
+
+
+QUrl Nepomuk::DataManagementModel::findApplicationResource(const QString &app, bool create)
+{
+    Soprano::QueryResultIterator it =
+            executeQuery(QString::fromLatin1("select ?r where { ?r a %1 . ?r %2 %3 . } LIMIT 1")
+                         .arg(Soprano::Node::resourceToN3(NAO::Agent()),
+                              Soprano::Node::resourceToN3(NAO::identifier()),
+                              Soprano::Node::literalToN3(app)),
+                         Soprano::Query::QueryLanguageSparql);
+    if(it.next()) {
+        return it[0].uri();
+    }
+    else if(create) {
+        const QUrl graph = createUri(GraphUri);
+        const QUrl metadatagraph = createUri(GraphUri);
+        const QUrl uri = createUri(ResourceUri);
+
+        // graph metadata
+        addStatement( metadatagraph, NRL::coreGraphMetadataFor(), graph, metadatagraph );
+        addStatement( metadatagraph, RDF::type(), NRL::GraphMetadata(), metadatagraph );
+        addStatement( graph, RDF::type(), NRL::InstanceBase(), metadatagraph );
+        addStatement( graph, NAO::created(), Soprano::LiteralValue(QDateTime::currentDateTime()), metadatagraph );
+
+        // the app itself
+        addStatement( uri, RDF::type(), NAO::Agent(), graph );
+        addStatement( uri, NAO::identifier(), Soprano::LiteralValue(app), graph );
+
+        KService::List services = KServiceTypeTrader::self()->query(QLatin1String("Application"),
+                                                                    QString::fromLatin1("DesktopEntryName=='%1'").arg(app));
+        if(services.count() == 1) {
+            addStatement(uri, NAO::prefLabel(), Soprano::LiteralValue(services.first()->name()), graph);
+        }
+
+        return uri;
+    }
+    else {
+        return QUrl();
+    }
+}
+
+QUrl Nepomuk::DataManagementModel::createUri(Nepomuk::DataManagementModel::UriType type)
+{
+    QString typeToken;
+    if(type == GraphUri)
+        typeToken = QLatin1String("ctx");
+    else
+        typeToken = QLatin1String("res");
+
+    while( 1 ) {
+        QString uuid = QUuid::createUuid().toString();
+        uuid = uuid.mid(1, uuid.length()-2);
+        const QUrl uri = QUrl( QLatin1String("nepomuk:/") + typeToken + QLatin1String("/") + uuid );
+        if ( !FilterModel::executeQuery( QString::fromLatin1("ask where { "
+                                                             "{ %1 ?p1 ?o1 . } "
+                                                             "UNION "
+                                                             "{ ?s2 %1 ?o2 . } "
+                                                             "UNION "
+                                                             "{ ?s3 ?p3 %1 . } "
+                                                             "UNION "
+                                                             "{ graph %1 { ?s4 ?4 ?o4 . } . } "
+                                                             "}")
+                                        .arg( Soprano::Node::resourceToN3(uri) ), Soprano::Query::QueryLanguageSparql ).boolValue() ) {
+            return uri;
+        }
+    }
+}
+
+
+// TODO: emit resource watcher resource creation signals
+void Nepomuk::DataManagementModel::addProperty(const QHash<QUrl, QUrl> &resources, const QUrl &property, const QHash<Soprano::Node, Soprano::Node> &nodes, const QString &app)
+{
+    kDebug() << resources << property << nodes << app;
+    Q_ASSERT(!resources.isEmpty());
+    Q_ASSERT(!nodes.isEmpty());
+    Q_ASSERT(!property.isEmpty());
+
+    //
+    // Check cardinality conditions
+    //
+    const int maxCardinality = d->m_classAndPropertyTree->maxCardinality(property);
+    if( maxCardinality == 1 ) {
+        if( nodes.size() != 1 ) {
+            setError(QString::fromLatin1("%1 has cardinality of 1. Cannot set more then one value.").arg(property.toString()), Soprano::Error::ErrorInvalidArgument);
+            return;
+        }
+    }
+
+
+    clearError();
+
+
+    //
+    // Resolve file URLs
+    //
+    QUrl graph;
+    QSet<Soprano::Node> resolvedNodes;
+    QHash<Soprano::Node, Soprano::Node>::const_iterator end = nodes.constEnd();
+    for(QHash<Soprano::Node, Soprano::Node>::const_iterator it = nodes.constBegin();
+        it != end; ++it) {
+        if(it.value().isEmpty()) {
+            if(graph.isEmpty()) {
+                graph = createGraph( app );
+                if(!graph.isValid()) {
+                    // error has been set in createGraph
+                    return;
+                }
+            }
+            const QUrl uri = createUri(ResourceUri);
+            addStatement(uri, Vocabulary::NIE::url(), it.key(), graph);
+            if(it.key().uri().scheme() == QLatin1String("file")) {
+                addStatement(uri, RDF::type(), NFO::FileDataObject(), graph);
+            }
+            resolvedNodes.insert(uri);
+        }
+        else {
+            resolvedNodes.insert(it.value());
+        }
+    }
+
+    QSet<QPair<QUrl, Soprano::Node> > finalProperties;
+    QList<QUrl> knownResources;
+    for(QHash<QUrl, QUrl>::const_iterator it = resources.constBegin();
+        it != resources.constEnd(); ++it) {
+        QUrl uri = it.value();
+        if(uri.isEmpty()) {
+            if(graph.isEmpty()) {
+                graph = createGraph( app );
+                if(!graph.isValid()) {
+                    // error has been set in createGraph
+                    return;
+                }
+            }
+            uri = createUri(ResourceUri);
+            addStatement(uri, Vocabulary::NIE::url(), it.key(), graph);
+            if(it.key().scheme() == QLatin1String("file")) {
+                addStatement(uri, RDF::type(), NFO::FileDataObject(), graph);
+            }
+        }
+        else {
+            knownResources << uri;
+        }
+        Q_FOREACH(const Soprano::Node& node, resolvedNodes) {
+            finalProperties << qMakePair(uri, node);
+        }
+    }
+
+    const QUrl appRes = findApplicationResource(app);
+
+
+    //
+    // Check if values already exist. If so remove the resources from the resourceSet and only add the application
+    // as maintainedBy in a new graph (except if its the only statement in the graph)
+    //
+    if(!knownResources.isEmpty()) {
+        const QString existingValuesQuery = QString::fromLatin1("select distinct ?r ?v ?g ?m "
+                                                                "(select count(*) where { graph ?g { ?s ?p ?o . } . FILTER(%5) . }) as ?cnt "
+                                                                "where { "
+                                                                "graph ?g { ?r %2 ?v . } . "
+                                                                "?m %1 ?g . "
+                                                                "FILTER(?r in (%3)) . "
+                                                                "FILTER(?v in (%4)) . }")
+                .arg(Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()),
+                     Soprano::Node::resourceToN3(property),
+                     resourcesToN3(knownResources).join(QLatin1String(",")),
+                     nodesToN3(resolvedNodes).join(QLatin1String(",")),
+                     createResourceMetadataPropertyFilter(QLatin1String("?p")));
+        QList<Soprano::BindingSet> existingValueBindings = executeQuery(existingValuesQuery, Soprano::Query::QueryLanguageSparql).allBindings();
+        Q_FOREACH(const Soprano::BindingSet& binding, existingValueBindings) {
+            kDebug() << "Existing value" << binding;
+            const QUrl r = binding["r"].uri();
+            const Soprano::Node v = binding["v"];
+            const QUrl g = binding["g"].uri();
+            const QUrl m = binding["m"].uri();
+            const int cnt = binding["cnt"].literal().toInt();
+
+            // we handle this property here - thus, no need to handle it below
+            finalProperties.remove(qMakePair(r, v));
+
+            // in case the app is the same there is no need to do anything
+            if(containsStatement(g, NAO::maintainedBy(), appRes, m)) {
+                continue;
+            }
+            else if(cnt == 1) {
+                // we can reuse the graph
+                addStatement(g, NAO::maintainedBy(), appRes, m);
+            }
+            else {
+                // we need to split the graph
+                // FIXME: do not split the same graph again and again. Check if the graph in question already is the one we created.
+                const QUrl newGraph = splitGraph(g, m, appRes);
+
+                // and finally move the actual property over to the new graph
+                removeStatement(r, property, v, g);
+                addStatement(r, property, v, newGraph);
+            }
+        }
+    }
+
+
+    //
+    // All conditions have been checked - create the actual data
+    //
+    if(!finalProperties.isEmpty()) {
+        if(graph.isEmpty()) {
+            graph = createGraph( app );
+            if(!graph.isValid()) {
+                // error has been set in createGraph
+                return;
+            }
+        }
+
+        // add all the data
+        // TODO: check if using one big sparql insert improves performance
+        QSet<QUrl> finalResources;
+        for(QSet<QPair<QUrl, Soprano::Node> >::const_iterator it = finalProperties.constBegin(); it != finalProperties.constEnd(); ++it) {
+            addStatement(it->first, property, it->second, graph);
+            if(property == NIE::url() && it->second.uri().scheme() == QLatin1String("file")) {
+                addStatement(it->first, RDF::type(), NFO::FileDataObject(), graph);
+            }
+            finalResources.insert(it->first);
+            d->m_watchManager->addProperty(it->first, property, it->second);
+        }
+
+        // update modification date
+        Q_FOREACH(const QUrl& res, finalResources) {
+            updateModificationDate(res, graph, QDateTime::currentDateTime(), true);
+        }
+    }
+}
+
+bool Nepomuk::DataManagementModel::doesResourceExist(const QUrl &res, const QUrl& graph) const
+{
+    if(graph.isEmpty()) {
+        return executeQuery(QString::fromLatin1("ask where { %1 ?p ?v . FILTER(%2) . }")
+                            .arg(Soprano::Node::resourceToN3(res),
+                                 createResourceMetadataPropertyFilter(QLatin1String("?p"))),
+                            Soprano::Query::QueryLanguageSparql).boolValue();
+    }
+    else {
+        return executeQuery(QString::fromLatin1("ask where { graph %1 { %2 ?p ?v . FILTER(%3) . } . }")
+                            .arg(Soprano::Node::resourceToN3(graph),
+                                 Soprano::Node::resourceToN3(res),
+                                 createResourceMetadataPropertyFilter(QLatin1String("?p"))),
+                            Soprano::Query::QueryLanguageSparql).boolValue();
+    }
+}
+
+QHash<QUrl, QUrl> Nepomuk::DataManagementModel::resolveUrls(const QList<QUrl> &urls) const
+{
+    QHash<QUrl, QUrl> uriHash;
+    Q_FOREACH(const QUrl& url, urls) {
+        const QUrl resolved = resolveUrl(url, true);
+        if(url.isEmpty() && lastError()) {
+            break;
+        }
+        uriHash.insert(url, resolved);
+    }
+    return uriHash;
+}
+
+QUrl Nepomuk::DataManagementModel::resolveUrl(const QUrl &url, bool statLocalFiles) const
+{
+    const UriState state = uriState( url, statLocalFiles );
+
+    if( state == NepomukUri || state == OntologyUri ) {
+        // nothing to resolve over here
+        return url;
+    }
+
+    //
+    // First check if the URL does exists as resource URI
+    //
+    else if( executeQuery(QString::fromLatin1("ask where { %1 ?p ?o . }")
+                     .arg(Soprano::Node::resourceToN3(url)),
+                     Soprano::Query::QueryLanguageSparql).boolValue() ) {
+        return url;
+    }
+
+    //
+    // we resolve all URLs except nepomuk:/ URIs. While the DMS does only use nie:url
+    // on local files libnepomuk used to use it for everything but nepomuk:/ URIs.
+    // Thus, we need to handle that legacy data by checking if url does exist as nie:url
+    //
+    else {
+        Soprano::QueryResultIterator it
+                = executeQuery(QString::fromLatin1("select ?r where { ?r %1 %2 . } limit 1")
+                               .arg(Soprano::Node::resourceToN3(NIE::url()),
+                                    Soprano::Node::resourceToN3(url)),
+                               Soprano::Query::QueryLanguageSparql);
+
+        // if the URL is used as a nie:url return the corresponding resource URI
+        if(it.next()) {
+            return it[0].uri();
+        }
+
+        // non-existing unsupported URL
+        else if( state == OtherUri ) {
+            QString error = QString::fromLatin1("Unknown protocol '%1' encountered in %2")
+                            .arg(url.scheme(), url.toString());
+            setError(error, Soprano::Error::ErrorInvalidArgument);
+            return QUrl();
+        }
+
+        // if there is no existing URI return an empty match for local files (since we do always want to use nie:url here)
+        else {
+            // we only throw an error if the file:/ URL points to a non-existing file AND it does not exist in the database.
+            if(state == NonExistingFileUrl) {
+                setError(QString::fromLatin1("Cannot store information about non-existing local files. File '%1' does not exist.").arg(url.toLocalFile()),
+                         Soprano::Error::ErrorInvalidArgument);
+            }
+
+            return QUrl();
+        }
+    }
+
+    // fallback
+    return url;
+}
+
+QHash<Soprano::Node, Soprano::Node> Nepomuk::DataManagementModel::resolveNodes(const QSet<Soprano::Node> &nodes) const
+{
+    QHash<Soprano::Node, Soprano::Node> resolvedNodes;
+    Q_FOREACH(const Soprano::Node& node, nodes) {
+        if(node.isResource()) {
+            resolvedNodes.insert(node, resolveUrl(node.uri(), true));
+        }
+        else {
+            resolvedNodes.insert(node, node);
+        }
+    }
+    return resolvedNodes;
+}
+
+bool Nepomuk::DataManagementModel::updateNieUrlOnLocalFile(const QUrl &resource, const QUrl &nieUrl)
+{
+    kDebug() << resource << "->" << nieUrl;
+
+    //
+    // Since KDE 4.4 we use nepomuk:/res/<UUID> Uris for all resources including files. Thus, moving a file
+    // means updating two things:
+    // 1. the nie:url property
+    // 2. the nie:isPartOf relation (only necessary if the file and not the whole folder was moved)
+    //
+
+    //
+    // Now update the nie:url, nfo:fileName, and nie:isPartOf relations.
+    //
+    // We do NOT use setProperty to avoid the overhead and data clutter of creating
+    // new metadata graphs for the changed data.
+    //
+
+    // remember for url, filename, and parent the graph they are defined in
+    QUrl resUri, oldNieUrl, oldNieUrlGraph, oldParentResource, oldParentResourceGraph, oldFileNameGraph;
+    QString oldFileName;
+
+    // we do not use isLocalFileUrl() here since we also handle already moved files
+    if(resource.scheme() == QLatin1String("file")) {
+        oldNieUrl = resource;
+        Soprano::QueryResultIterator it
+                = executeQuery(QString::fromLatin1("select distinct ?gu ?gf ?gp ?r ?f ?p where { "
+                                                   "graph ?gu { ?r %2 %1 . } . "
+                                                   "OPTIONAL { graph ?gf { ?r %3 ?f . } . } . "
+                                                   "OPTIONAL { graph ?gp { ?r %4 ?p . } . } . "
+                                                   "} LIMIT 1")
+                               .arg(Soprano::Node::resourceToN3(resource),
+                                    Soprano::Node::resourceToN3(NIE::url()),
+                                    Soprano::Node::resourceToN3(NFO::fileName()),
+                                    Soprano::Node::resourceToN3(NIE::isPartOf())),
+                               Soprano::Query::QueryLanguageSparql);
+        if(it.next()) {
+            resUri= it["r"].uri();
+            oldNieUrlGraph = it["gu"].uri();
+            oldParentResource = it["p"].uri();
+            oldParentResourceGraph = it["gp"].uri();
+            oldFileName = it["f"].toString();
+            oldFileNameGraph = it["gf"].uri();
+        }
+    }
+    else {
+        resUri = resource;
+        Soprano::QueryResultIterator it
+                = executeQuery(QString::fromLatin1("select distinct ?gu ?gf ?gp ?u ?f ?p where { "
+                                                   "graph ?gu { %1 %2 ?u . } . "
+                                                   "OPTIONAL { graph ?gf { %1 %3 ?f . } . } . "
+                                                   "OPTIONAL { graph ?gp { %1 %4 ?p . } . } . "
+                                                   "} LIMIT 1")
+                               .arg(Soprano::Node::resourceToN3(resource),
+                                    Soprano::Node::resourceToN3(NIE::url()),
+                                    Soprano::Node::resourceToN3(NFO::fileName()),
+                                    Soprano::Node::resourceToN3(NIE::isPartOf())),
+                               Soprano::Query::QueryLanguageSparql);
+        if(it.next()) {
+            oldNieUrl = it["u"].uri();
+            oldNieUrlGraph = it["gu"].uri();
+            oldParentResource = it["p"].uri();
+            oldParentResourceGraph = it["gp"].uri();
+            oldFileName = it["f"].toString();
+            oldFileNameGraph = it["gf"].uri();
+        }
+    }
+
+    if (!oldNieUrlGraph.isEmpty()) {
+        removeStatement(resUri, NIE::url(), oldNieUrl, oldNieUrlGraph);
+        addStatement(resUri, NIE::url(), nieUrl, oldNieUrlGraph);
+
+        if (!oldFileNameGraph.isEmpty()) {
+            // we only update the filename if it actually changed
+            if(KUrl(oldNieUrl).fileName() != KUrl(nieUrl).fileName()) {
+                removeStatement(resUri, NFO::fileName(), Soprano::LiteralValue(oldFileName), oldFileNameGraph);
+                addStatement(resUri, NFO::fileName(), Soprano::LiteralValue(KUrl(nieUrl).fileName()), oldFileNameGraph);
+            }
+        }
+
+        if (!oldParentResourceGraph.isEmpty()) {
+            // we only update the parent folder if it actually changed
+            const KUrl nieUrlParent = KUrl(nieUrl).directory(KUrl::IgnoreTrailingSlash);
+            const KUrl oldUrlParent = KUrl(oldNieUrl).directory(KUrl::IgnoreTrailingSlash);
+            if(nieUrlParent != oldUrlParent) {
+                removeStatement(resUri, NIE::isPartOf(), oldParentResource, oldParentResourceGraph);
+                const QUrl newParentRes = resolveUrl(nieUrlParent);
+                if (!newParentRes.isEmpty()) {
+                    addStatement(resUri, NIE::isPartOf(), newParentRes, oldParentResourceGraph);
+                }
+            }
+        }
+
+        //
+        // Update all children
+        // We only need to update the nie:url properties. Filenames and nie:isPartOf relations cannot change
+        // due to a renaming of the parent folder.
+        //
+        // CAUTION: The trailing slash on the from URL is essential! Otherwise we might match the newly added
+        //          URLs, too (in case a rename only added chars to the name)
+        //
+        const QString query = QString::fromLatin1("select distinct ?r ?u ?g where { "
+                                                  "graph ?g { ?r %1 ?u . } . "
+                                                  "FILTER(REGEX(STR(?u),'^%2')) . "
+                                                  "}")
+                .arg(Soprano::Node::resourceToN3(NIE::url()),
+                     KUrl(oldNieUrl).url(KUrl::AddTrailingSlash));
+
+        const QString oldBasePath = KUrl(oldNieUrl).path(KUrl::AddTrailingSlash);
+        const QString newBasePath = KUrl(nieUrl).path(KUrl::AddTrailingSlash);
+
+        //
+        // We cannot use one big loop since our updateMetadata calls below can change the iterator
+        // which could have bad effects like row skipping. Thus, we handle the urls in chunks of
+        // cached items.
+        //
+        forever {
+            const QList<Soprano::BindingSet> urls = executeQuery(query + QLatin1String( " LIMIT 500" ),
+                                                                 Soprano::Query::QueryLanguageSparql)
+                    .allBindings();
+            if (urls.isEmpty())
+                break;
+
+            for (int i = 0; i < urls.count(); ++i) {
+                const KUrl u = urls[i]["u"].uri();
+                const QUrl r = urls[i]["r"].uri();
+                const QUrl g = urls[i]["g"].uri();
+
+                // now construct the new URL
+                const QString oldRelativePath = u.path().mid(oldBasePath.length());
+                const KUrl newUrl(newBasePath + oldRelativePath);
+
+                removeStatement(r, NIE::url(), u, g);
+                addStatement(r, NIE::url(), newUrl, g);
+            }
+        }
+
+        return true;
+    }
+    else {
+        // no old nie:url found
+        return false;
+    }
+}
+
+//void Nepomuk::DataManagementModel::insertStatements(const QSet<QUrl> &resources, const QUrl &property, const QSet<Soprano::Node> &values, const QUrl &graph)
+//{
+//    const QString propertyString = Soprano::Node::resourceToN3(property);
+
+//    QString query = QString::fromLatin1("insert into %1 { ")
+//            .arg(Soprano::Node::resourceToN3(graph));
+
+//    foreach(const QUrl& res, resources) {
+//        foreach(const Soprano::Node& value, values) {
+//            query.append(QString::fromLatin1("%1 %2 %3 . ")
+//                        .arg(Soprano::Node::resourceToN3(res),
+//                             propertyString,
+//                             value.toN3()));
+//        }
+//    }
+//    query.append(QLatin1String("}"));
+
+//    executeQuery(query, Soprano::Query::QueryLanguageSparql);
+//}
+
+Nepomuk::ClassAndPropertyTree* Nepomuk::DataManagementModel::classAndPropertyTree()
+{
+    return d->m_classAndPropertyTree;
+}
+
+bool Nepomuk::DataManagementModel::containsResourceWithProtectedType(const QSet<QUrl> &resources) const
+{
+    if(executeQuery(QString::fromLatin1("ask where { ?r a ?t . FILTER(?r in (%1)) . FILTER(?t in (%2,%3,%4)) . }")
+            .arg(resourcesToN3(resources).join(QLatin1String(",")),
+                 Soprano::Node::resourceToN3(RDFS::Class()),
+                 Soprano::Node::resourceToN3(RDF::Property()),
+                 Soprano::Node::resourceToN3(NRL::Graph())),
+                    Soprano::Query::QueryLanguageSparql).boolValue()) {
+        setError(QLatin1String("It is not allowed to remove classes, properties, or graphs through this API."), Soprano::Error::ErrorInvalidArgument);
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+Nepomuk::ResourceWatcherManager* Nepomuk::DataManagementModel::resourceWatcherManager() const
+{
+    return d->m_watchManager;
+}
+
+void Nepomuk::DataManagementModel::removeAllResources(const QSet<QUrl> &resources, RemovalFlags flags)
+{
+    QSet<QUrl> resolvedResources(resources);
+
+    //
+    // Handle the sub-resources:
+    // this has to be done before deleting the resouces in resolvedResources. Otherwise the nao:hasSubResource relationships are already gone!
+    //
+    // Explanation of the query:
+    // The query selects all subresources of the resources in resolvedResources.
+    // It then filters out the sub-resources that are related from other resources that are not the ones being deleted.
+    //
+    if(flags & RemoveSubResoures) {
+        QSet<QUrl> subResources = resolvedResources;
+        int resCount = 0;
+        do {
+            resCount = resolvedResources.count();
+            Soprano::QueryResultIterator it
+                    = executeQuery(QString::fromLatin1("select ?r where { ?r ?p ?o . "
+                                                       "?parent %1 ?r . "
+                                                       "FILTER(?parent in (%2)) . "
+                                                       "FILTER(!bif:exists((select (1) where { ?r2 ?p3 ?r . FILTER(%3) . FILTER(!bif:exists((select (1) where { ?x %1 ?r2 . FILTER(?x in (%2)) . }))) . }))) . "
+                                                       "}")
+                                   .arg(Soprano::Node::resourceToN3(NAO::hasSubResource()),
+                                        resourcesToN3(subResources).join(QLatin1String(",")),
+                                        createResourceFilter(resolvedResources, QLatin1String("?r2"))),
+                                   Soprano::Query::QueryLanguageSparql);
+            subResources.clear();
+            while(it.next()) {
+                subResources << it[0].uri();
+            }
+            resolvedResources += subResources;
+        } while(resCount < resolvedResources.count());
+    }
+
+
+    // get the graphs we need to check with removeTrailingGraphs later on
+    QSet<QUrl> graphs;
+    QSet<QUrl> actuallyRemovedResources;
+    Soprano::QueryResultIterator it
+            = executeQuery(QString::fromLatin1("select distinct ?g ?r where { graph ?g { ?r ?p ?o . } . FILTER(?r in (%1)) . }")
+                           .arg(resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                           Soprano::Query::QueryLanguageSparql);
+    while(it.next()) {
+        graphs << it[0].uri();
+        actuallyRemovedResources << it[1].uri();
+    }
+
+    // get the resources that we modify by removing relations to one of the deleted ones
+    QSet<QUrl> modifiedResources;
+    it = executeQuery(QString::fromLatin1("select distinct ?g ?o where { graph ?g { ?o ?p ?r . } . FILTER(?r in (%1)) . }")
+                      .arg(resourcesToN3(resolvedResources).join(QLatin1String(","))),
+                      Soprano::Query::QueryLanguageSparql);
+    while(it.next()) {
+        graphs << it[0].uri();
+        modifiedResources << it[1].uri();
+    }
+    modifiedResources -= actuallyRemovedResources;
+
+
+    // remove the resources
+    foreach(const Soprano::Node& res, actuallyRemovedResources) {
+        removeAllStatements(res, Soprano::Node(), Soprano::Node());
+        removeAllStatements(Soprano::Node(), Soprano::Node(), res);
+    }
+    updateModificationDate(modifiedResources);
+    removeTrailingGraphs(graphs);
+
+    // inform interested parties
+    // TODO: ideally we should also report the types the removed resources had
+    foreach(const Soprano::Node& res, actuallyRemovedResources) {
+        d->m_watchManager->removeResource(res.uri(), QList<QUrl>());
+    }
+}
+
+#include "datamanagementmodel.moc"
diff --git a/nepomuk/services/storage/datamanagementmodel.h b/nepomuk/services/storage/datamanagementmodel.h
new file mode 100644
index 0000000..9722157
--- /dev/null
+++ b/nepomuk/services/storage/datamanagementmodel.h
@@ -0,0 +1,324 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DATAMANAGEMENTMODEL_H
+#define DATAMANAGEMENTMODEL_H
+
+#include "lib/datamanagement.h"
+
+#include <Soprano/FilterModel>
+
+#include <QtCore/QDateTime>
+
+namespace Nepomuk {
+
+class ClassAndPropertyTree;
+class ResourceMerger;
+class SimpleResourceGraph;
+class ResourceWatcherManager;
+
+class DataManagementModel : public Soprano::FilterModel
+{
+    Q_OBJECT
+
+public:
+    DataManagementModel(ClassAndPropertyTree* tree, Soprano::Model* model, QObject *parent = 0);
+    ~DataManagementModel();
+
+    /// used by the unit tests
+    ResourceWatcherManager* resourceWatcherManager() const;
+
+public Q_SLOTS:
+    /**
+     * \name Basic API
+     */
+    //@{
+    /**
+     * Add \p property with \p values to each resource
+     * from \p resources. Existing values will not be touched.
+     * If a cardinality is breached an error will be thrown.
+     */
+    void addProperty(const QList<QUrl>& resources,
+                     const QUrl& property,
+                     const QVariantList& values,
+                     const QString& app);
+
+    /**
+     * Set, ie. overwrite properties. Set \p property with
+     * \p values for each resource from \p resources. Existing
+     * values will be replaced.
+     */
+    void setProperty(const QList<QUrl>& resources,
+                     const QUrl& property,
+                     const QVariantList& values,
+                     const QString& app);
+
+    /**
+     * Remove the property \p property with \p values from each
+     * resource in \p resources.
+     */
+    void removeProperty(const QList<QUrl>& resources,
+                        const QUrl& property,
+                        const QVariantList& values,
+                        const QString& app);
+
+    /**
+     * Remove all statements involving any proerty from \p properties from
+     * all resources in \p resources.
+     */
+    void removeProperties(const QList<QUrl>& resources,
+                          const QList<QUrl>& properties,
+                          const QString& app);
+
+    /**
+     * Create a new resource with several \p types.
+     */
+    QUrl createResource(const QList<QUrl>& types,
+                        const QString& label,
+                        const QString& description,
+                        const QString& app);
+
+    /**
+     * Remove resources from the database.
+     * \param resources The URIs of the resources to be removed.
+     * \param app The calling application.
+     * \param force Force deletion of the resource and all sub-resources.
+     * If false sub-resources will be kept if they are still referenced by
+     * other resources.
+     */
+    void removeResources(const QList<QUrl>& resources,
+                         Nepomuk::RemovalFlags flags,
+                         const QString& app);
+    //@}
+
+    /**
+     * \name Advanced API
+     */
+    //@{
+    /**
+     * Remove all information about resources from the database which
+     * have been created by a specific application.
+     * \param resources The URIs of the resources to be removed.
+     * \param app The application for which data should be removed.
+     * \param force Force deletion of the resource and all sub-resources.
+     * If false sub-resources will be kept if they are still referenced by
+     * other resources.
+     */
+    void removeDataByApplication(const QList<QUrl>& resources,
+                                 RemovalFlags flags,
+                                 const QString& app);
+
+    /**
+     * Remove all information from the database which
+     * has been created by a specific application.
+     * \param app The application for which data should be removed.
+     * \param force Force deletion of the resource and all sub-resources.
+     * If false sub-resources will be kept if they are still referenced by
+     * resources that have been created by other applications.
+     */
+    void removeDataByApplication(RemovalFlags flags,
+                                 const QString& app);
+
+    /**
+     * \param resources The resources to be merged. Blank nodes will be converted into new
+     * URIs (unless the corresponding resource already exists).
+     * \param identificationMode This method can try hard to avoid duplicate resources by looking
+     * for already existing duplicates based on nrl:IdentifyingProperty. By default it only looks
+     * for duplicates of resources that do not have a resource URI (SimpleResource::uri()) defined.
+     * This behaviour can be changed with this parameter.
+     * \param flags Additional flags to change the behaviour of the method.
+     * \param additionalMetadata Additional metadata for the added resources. This can include
+     * such details as the creator of the data or details on the method of data recovery.
+     * One typical usecase is that the file indexer uses (rdf:type, nrl:DiscardableInstanceBase)
+     * to state that the provided information can be recreated at any time. Only built-in types
+     * such as int, string, or url are supported.
+     * \param app The calling application
+     */
+    void storeResources(const SimpleResourceGraph& resources,
+                        const QString& app,
+                        Nepomuk::StoreIdentificationMode identificationMode = Nepomuk::IdentifyNew,
+                        Nepomuk::StoreResourcesFlags flags = Nepomuk::NoStoreResourcesFlags,
+                        const QHash<QUrl, QVariant>& additionalMetadata = QHash<QUrl, QVariant>() );
+
+    /**
+     * Merges two resources into one. Properties from \p resource1
+     * take precedence over that from \p resource2 (for properties with cardinality 1).
+     */
+    void mergeResources(const QUrl& resource1, const QUrl& resource2, const QString& app);
+
+    /**
+     * Import an RDF graph from a URL.
+     * \param url The url from which the graph should be loaded. This does not have to be local.
+     * \param serialization The RDF serialization used for the file. If Soprano::SerializationUnknown a crude automatic
+     * detection based on file extension is used.
+     * \param userSerialization If \p serialization is Soprano::SerializationUser this value is used. See Soprano::Parser
+     * for details.
+     * \param identificationMode This method can try hard to avoid duplicate resources by looking
+     * for already existing duplicates based on nrl:IdentifyingProperty. By default it only looks
+     * for duplicates of resources that do not have a resource URI (SimpleResource::uri()) defined.
+     * This behaviour can be changed with this parameter.
+     * \param flags Additional flags to change the behaviour of the method.
+     * \param additionalMetadata Additional metadata for the added resources. This can include
+     * such details as the creator of the data or details on the method of data recovery.
+     * One typical usecase is that the file indexer uses (rdf:type, nrl:DiscardableInstanceBase)
+     * to state that the provided information can be recreated at any time. Only built-in types
+     * such as int, string, or url are supported.
+     * \param app The calling application
+     */
+    void importResources(const QUrl& url, const QString& app,
+                         Soprano::RdfSerialization serialization,
+                         const QString& userSerialization = QString(),
+                         Nepomuk::StoreIdentificationMode identificationMode = Nepomuk::IdentifyNew,
+                         Nepomuk::StoreResourcesFlags flags = Nepomuk::NoStoreResourcesFlags,
+                         const QHash<QUrl, QVariant>& additionalMetadata = QHash<QUrl, QVariant>());
+
+    /**
+     * Describe a set of resources, i.e. retrieve all their properties.
+     * \param resources The resource URIs of the resources to describe.
+     * \param includeSubResources If \p true sub resources will be included.
+     */
+    SimpleResourceGraph describeResources(const QList<QUrl>& resources,
+                                          bool includeSubResources) const;
+    //@}
+
+private:
+    QUrl createGraph(const QString& app = QString(), const QHash<QUrl, QVariant>& additionalMetadata = QHash<QUrl, QVariant>());
+    QUrl createGraph(const QString& app, const QMultiHash<QUrl, Soprano::Node>& additionalMetadata);
+
+    /**
+     * Splits \p graph into two. This essentially copies the graph metadata to a new graph and metadata graph pair.
+     * The newly created graph will set as being maintained by \p appRes.
+     *
+     * \param graph The graph that should be split/copied.
+     * \param metadataGraph The metadata graph of graph. This can be empty.
+     * \param appRes The application resource which will be added as maintaining the newly created graph. Can be empty.
+     *
+     * \return The URI of the newly created graph.
+     */
+    QUrl splitGraph(const QUrl& graph, const QUrl& metadataGraph, const QUrl& appRes);
+
+    QUrl findApplicationResource(const QString& app, bool create = true);
+
+    /**
+     * Updates the modification date of \p resource to \p date.
+     * Adds the new statement in \p graph.
+     */
+    Soprano::Error::ErrorCode updateModificationDate( const QUrl& resource, const QUrl& graph = QUrl(), const QDateTime& date = QDateTime::currentDateTime(), bool includeCreationDate = false );
+
+    /**
+     * Updates the modification date of \p resources to \p date.
+     * Adds the new statement in \p graph.
+     */
+    Soprano::Error::ErrorCode updateModificationDate( const QSet<QUrl>& resources, const QUrl& graph = QUrl(), const QDateTime& date = QDateTime::currentDateTime(), bool includeCreationDate = false );
+
+    /**
+     * Removes all the graphs from \p graphs which do not contain any statements
+     */
+    void removeTrailingGraphs( const QSet<QUrl>& graphs );
+
+    /**
+     * Adds for each resource in \p resources a property for each node in nodes. \p nodes cannot be empty.
+     * This method is used in the public setProperty and addProperty slots to avoid a lot of code duplication.
+     *
+     * \param resources A hash mapping the resources provided by the client to the actual resource URIs. This hash is created via resolveUrls() and can
+     *                  contain empty values which means that the resource corresponding to a file URL does not exist yet.
+     *                  This hash cannot be empty.
+     * \param property The property to use. This cannot be empty.
+     * \param nodes A hash mapping value nodes as created via resolveNodes from the output of ClassAndPropertyTree::variantToNodeSet. Like \p resources
+     *              this hash might contain empty values which refer to non-existing file resources. This cannot be empty.
+     * \param app The calling application.
+     */
+    void addProperty(const QHash<QUrl, QUrl>& resources, const QUrl& property, const QHash<Soprano::Node, Soprano::Node>& nodes, const QString& app);
+
+    /**
+     * Removes the given resources without any additional checks. The provided list needs to contain already resolved valid resource URIs.
+     *
+     * Used by removeResources() and removeDataByApplication()
+     */
+    void removeAllResources(const QSet<QUrl>& resourceUris, RemovalFlags flags);
+
+    /**
+     * Checks if resource \p res actually exists. A resource exists if any information other than the standard metadata
+     * (nao:created, nao:creator, nao:lastModified, nao:userVisible) or the nie:url is defined.
+     */
+    bool doesResourceExist(const QUrl& res, const QUrl& graph = QUrl()) const;
+
+    /**
+     * Resolves a local file url to its resource URI. Returns \p url if it is not a file URL and
+     * an empty QUrl in case \p url is a file URL but has no resource URI in the model.
+     *
+     * \param statLocalFiles If \p true the method will check if local files exist and set an error
+     * if not.
+     */
+    QUrl resolveUrl(const QUrl& url, bool statLocalFiles = false) const;
+
+    /**
+     * Resolves local file URLs through nie:url.
+     * \return a Hash mapping \p urls to their actual resource URIs or an empty QUrl if the resource does not exist.
+     *
+     * This method does check if the local file exists and may set an error.
+     */
+    QHash<QUrl, QUrl> resolveUrls(const QList<QUrl>& urls) const;
+
+    /**
+     * Resolves local file URLs through nie:url.
+     * \return a Hash mapping \p nodes to the nodes that should actually be added to the model or an empty node if the resource for a file URL
+     * does not exist yet.
+     *
+     * This method does check if the local file exists and may set an error.
+     */
+    QHash<Soprano::Node, Soprano::Node> resolveNodes(const QSet<Soprano::Node>& nodes) const;
+
+    /**
+     * Updates the nie:url of a local file resource.
+     * \return \p true if the url has been updated and nothing else needs to be done, \p false
+     * if the update was not handled. An error also results in a return value of \p true.
+     *
+     * \param resource The resource to update. Both file URLs and resource URIs are supported. Thus, there is no need to resolve the URL
+     * before calling this method.
+     * \param nieUrl The new nie:url to assign to the resource.
+     */
+    bool updateNieUrlOnLocalFile(const QUrl& resource, const QUrl& nieUrl);
+
+    ClassAndPropertyTree * classAndPropertyTree();
+    
+    enum UriType {
+        GraphUri,
+        ResourceUri
+    };
+    QUrl createUri(UriType type);
+
+    /**
+     * Checks if any of the provided resources has a protected type (class, property, graph), ie. one
+     * of the resources should not be changed through the standard API.
+     *
+     * If the method returns \p true it has already set an appropriate error.
+     */
+    bool containsResourceWithProtectedType(const QSet<QUrl>& resources) const;
+
+    class Private;
+    Private* const d;
+
+    friend class ResourceMerger;
+};
+}
+
+#endif
diff --git a/nepomuk/services/storage/graphmaintainer.cpp b/nepomuk/services/storage/graphmaintainer.cpp
new file mode 100644
index 0000000..0415243
--- /dev/null
+++ b/nepomuk/services/storage/graphmaintainer.cpp
@@ -0,0 +1,96 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "graphmaintainer.h"
+
+#include <Soprano/Model>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/NodeIterator>
+#include <Soprano/BindingSet>
+#include <Soprano/Node>
+#include <Soprano/Statement>
+#include <Soprano/Vocabulary/NRL>
+
+#include <KDebug>
+
+
+using namespace Soprano::Vocabulary;
+
+Nepomuk::GraphMaintainer::GraphMaintainer(Soprano::Model* model)
+    : QThread(model),
+      m_model(model),
+      m_canceled(false)
+{
+}
+
+Nepomuk::GraphMaintainer::~GraphMaintainer()
+{
+    m_canceled = true;
+    wait();
+}
+
+void Nepomuk::GraphMaintainer::run()
+{
+    kDebug() << "Running graph maintenance";
+
+    m_canceled = false;
+
+    //
+    // query all empty graphs in batches
+    //
+    const QString query = QString::fromLatin1("select ?g ?mg where { "
+                                              "?g a %1 . "
+                                              "OPTIONAL { ?mg %2 ?g . } . "
+                                              "FILTER(!bif:exists((select (1) where { graph ?g { ?s ?p ?o . } . }))) . "
+                                              "} LIMIT 100")
+            .arg(Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                 Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()));
+
+    while(!m_canceled) {
+        QList<Soprano::BindingSet> graphs = m_model->executeQuery(query, Soprano::Query::QueryLanguageSparql).allBindings();
+        if(graphs.isEmpty()) {
+            break;
+        }
+        foreach(const Soprano::BindingSet& graph, graphs) {
+            const Soprano::Node g = graph["g"];
+            const Soprano::Node mg = graph["mg"];
+
+            if(m_canceled) {
+                break;
+            }
+
+            // clear the metadata graph
+            if(mg.isResource()) {
+                m_model->removeContext(mg);
+            }
+
+            // clear out anything that is left in crappy inference graphs
+            m_model->removeAllStatements(g, Soprano::Node(), Soprano::Node());
+
+            // wait a bit so we do not hog all CPU.
+            msleep(200);
+        }
+    }
+
+    kDebug() << "Finished graph maintenance";
+}
+
+#include "graphmaintainer.moc"
diff --git a/nepomuk/services/storage/graphmaintainer.h b/nepomuk/services/storage/graphmaintainer.h
new file mode 100644
index 0000000..8ca547f
--- /dev/null
+++ b/nepomuk/services/storage/graphmaintainer.h
@@ -0,0 +1,53 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef NEPOMUK_GRAPHMAINTAINER_H
+#define NEPOMUK_GRAPHMAINTAINER_H
+
+#include <QtCore/QThread>
+
+namespace Soprano {
+class Model;
+}
+
+namespace Nepomuk {
+
+/**
+ * The graph maintainer takes care of cleaning up danging data like
+ * empty graphs which have been left by old or buggy code.
+ */
+class GraphMaintainer : public QThread
+{
+    Q_OBJECT
+
+public:
+    GraphMaintainer(Soprano::Model *model);
+    ~GraphMaintainer();
+
+private:
+    void run();
+
+    Soprano::Model* m_model;
+    bool m_canceled;
+};
+}
+
+#endif // NEPOMUK_GRAPHMAINTAINER_H
diff --git a/nepomuk/services/storage/lib/CMakeLists.txt b/nepomuk/services/storage/lib/CMakeLists.txt
new file mode 100644
index 0000000..5848c4b
--- /dev/null
+++ b/nepomuk/services/storage/lib/CMakeLists.txt
@@ -0,0 +1,55 @@
+project(libdatamanagement)
+
+set(datamanagement_SRC
+  simpleresource.cpp
+  simpleresourcegraph.cpp
+  genericdatamanagementjob.cpp
+  createresourcejob.cpp
+  describeresourcesjob.cpp
+  dbustypes.cpp
+  datamanagement.cpp
+  resourcewatcher.cpp
+  abstracttimeoutdbusinterface.cpp
+  datamanagementinterface.cpp
+)
+
+qt4_add_dbus_interface(datamanagement_SRC
+  ../../../interfaces/org.kde.nepomuk.ResourceWatcherConnection.xml
+  resourcewatcherconnectioninterface)
+
+qt4_add_dbus_interface(datamanagement_SRC
+  ../../../interfaces/org.kde.nepomuk.ResourceWatcher.xml
+  resourcewatchermanagerinterface)
+
+kde4_add_library(nepomukdatamanagement SHARED
+  ${datamanagement_SRC})
+
+target_link_libraries(nepomukdatamanagement
+  ${QT_QTCORE_LIBRARY}
+  ${QT_QTDBUS_LIBRARY}
+  ${SOPRANO_LIBRARIES}
+  ${NEPOMUK_LIBRARIES}
+  ${KDE4_KDECORE_LIBS})
+
+install(TARGETS nepomukdatamanagement DESTINATION ${LIB_INSTALL_DIR})
+install(FILES
+  simpleresource.h
+  simpleresourcegraph.h
+  datamanagement.h
+  createresourcejob.h
+  describeresourcesjob.h
+  resourcewatcher.h
+  nepomukdatamanagement_export.h
+  DESTINATION ${INCLUDE_INSTALL_DIR}/nepomuk)
+
+
+# API docs
+find_package(Doxygen)
+
+if(DOXYGEN_EXECUTABLE)
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
+
+  add_custom_target(
+    dms-apidox
+    COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile)
+endif(DOXYGEN_EXECUTABLE)
diff --git a/nepomuk/services/storage/lib/Doxyfile.cmake b/nepomuk/services/storage/lib/Doxyfile.cmake
new file mode 100644
index 0000000..cf9d13f
--- /dev/null
+++ b/nepomuk/services/storage/lib/Doxyfile.cmake
@@ -0,0 +1,242 @@
+# Doxyfile 1.5.3
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           = Nepomuk Data Management
+PROJECT_NUMBER         = ${KDE_VERSION}
+OUTPUT_DIRECTORY       = docs
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = "The $name class " \
+                         "The $name widget " \
+                         "The $name file " \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+ALWAYS_DETAILED_SEC    = YES
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = ./
+STRIP_FROM_INC_PATH    = 
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = NO
+QT_AUTOBRIEF           = YES
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 4
+ALIASES                = 
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = NO
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+DISTRIBUTE_GROUP_DOC   = YES
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = NO
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = YES
+HIDE_FRIEND_COMPOUNDS  = YES
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = YES
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = NO
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = NO
+FILE_VERSION_FILTER    = 
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text "
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = ${CMAKE_CURRENT_SOURCE_DIR}
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          = *.h
+RECURSIVE              = NO
+EXCLUDE                = 
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = ${CMAKE_CURRENT_SOURCE_DIR}/*_p.h ${CMAKE_CURRENT_SOURCE_DIR}/dbustypes.h
+EXCLUDE_SYMBOLS        = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = *
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = 
+INPUT_FILTER           = 
+FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+HTML_DYNAMIC_SECTIONS  = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             = 
+XML_DTD                = 
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = YES
+EXPAND_ONLY_PREDEF     = YES
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = EIGEN_MAKE_DYNAMICSIZE_TYPEDEFS \
+                         EIGEN_MAKE_FIXEDSIZE_TYPEDEFS
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            = 
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = NO
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = YES
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = 
+DOTFILE_DIRS           = 
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 1000
+DOT_TRANSPARENT        = YES
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff --git a/nepomuk/services/storage/lib/abstracttimeoutdbusinterface.cpp b/nepomuk/services/storage/lib/abstracttimeoutdbusinterface.cpp
new file mode 100644
index 0000000..cc060d3
--- /dev/null
+++ b/nepomuk/services/storage/lib/abstracttimeoutdbusinterface.cpp
@@ -0,0 +1,42 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "abstracttimeoutdbusinterface.h"
+
+#include <QtDBus/QDBusPendingCall>
+
+AbstractTimeoutDBusInterface::AbstractTimeoutDBusInterface(const QString& service, const QString& path, const char* interface, const QDBusConnection& connection, QObject* parent)
+    : QDBusAbstractInterface( service, path, interface, connection, parent )
+{
+}
+
+AbstractTimeoutDBusInterface::~AbstractTimeoutDBusInterface()
+{
+}
+
+QDBusPendingCall AbstractTimeoutDBusInterface::asyncCallWithArgumentList(const QString &method, const QList<QVariant> &args, int timeout)
+{
+    QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), method);
+    msg.setArguments(args);
+    return connection().asyncCall(msg, timeout);
+}
+
+#include "abstracttimeoutdbusinterface.moc"
diff --git a/nepomuk/services/storage/lib/abstracttimeoutdbusinterface.h b/nepomuk/services/storage/lib/abstracttimeoutdbusinterface.h
new file mode 100644
index 0000000..3cd0f3f
--- /dev/null
+++ b/nepomuk/services/storage/lib/abstracttimeoutdbusinterface.h
@@ -0,0 +1,41 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ABSTRACTTIMEOUTDBUSINTERFACE_H
+#define ABSTRACTTIMEOUTDBUSINTERFACE_H
+
+#include <QDBusAbstractInterface>
+
+/**
+ * An extension of the abstract DBus interface which allows to set the timeout.
+ */
+class AbstractTimeoutDBusInterface : public QDBusAbstractInterface
+{
+    Q_OBJECT
+
+public:
+    AbstractTimeoutDBusInterface(const QString& service, const QString& path, const char* interface, const QDBusConnection& connection, QObject* parent = 0);
+    ~AbstractTimeoutDBusInterface();
+
+    QDBusPendingCall asyncCallWithArgumentList(const QString &method, const QList<QVariant> &args, int timeout);
+};
+
+#endif // ABSTRACTTIMEOUTDBUSINTERFACE_H
diff --git a/nepomuk/services/storage/lib/createresourcejob.cpp b/nepomuk/services/storage/lib/createresourcejob.cpp
new file mode 100644
index 0000000..fb7880e
--- /dev/null
+++ b/nepomuk/services/storage/lib/createresourcejob.cpp
@@ -0,0 +1,93 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "createresourcejob.h"
+#include "datamanagementinterface.h"
+#include "dbustypes.h"
+#include "genericdatamanagementjob_p.h"
+
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusPendingReply>
+#include <QtDBus/QDBusPendingCallWatcher>
+
+#include <QtCore/QVariant>
+#include <QtCore/QUrl>
+
+#include <KComponentData>
+#include <KUrl>
+
+
+class Nepomuk::CreateResourceJob::Private
+{
+public:
+    QUrl m_resourceUri;
+};
+
+Nepomuk::CreateResourceJob::CreateResourceJob(const QList<QUrl>& types,
+                                     const QString& label,
+                                     const QString& description,
+                                     const KComponentData& component)
+    : KJob(0),
+      d(new Private)
+{
+    org::kde::nepomuk::DataManagement dms(QLatin1String(DMS_DBUS_SERVICE),
+                                          QLatin1String("/datamanagement"),
+                                          QDBusConnection::sessionBus());
+    QDBusPendingCallWatcher* dbusCallWatcher
+            = new QDBusPendingCallWatcher(dms.createResource(Nepomuk::DBus::convertUriList(types),
+                                                             label,
+                                                             description,
+                                                             component.componentName()));
+    connect(dbusCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+            this, SLOT(slotDBusCallFinished(QDBusPendingCallWatcher*)));
+}
+
+Nepomuk::CreateResourceJob::~CreateResourceJob()
+{
+    delete d;
+}
+
+void Nepomuk::CreateResourceJob::start()
+{
+    // do nothing, we do everything in the constructor
+}
+
+void Nepomuk::CreateResourceJob::slotDBusCallFinished(QDBusPendingCallWatcher *watcher)
+{
+    QDBusPendingReply<QString> reply = *watcher;
+    if (reply.isError()) {
+        QDBusError error = reply.error();
+        setError(1);
+        setErrorText(error.message());
+    }
+    else {
+        d->m_resourceUri = KUrl(reply.value());
+    }
+    watcher->deleteLater();
+    emitResult();
+}
+
+QUrl Nepomuk::CreateResourceJob::resourceUri() const
+{
+    return d->m_resourceUri;
+}
+
+#include "createresourcejob.moc"
diff --git a/nepomuk/services/storage/lib/createresourcejob.h b/nepomuk/services/storage/lib/createresourcejob.h
new file mode 100644
index 0000000..190aba9
--- /dev/null
+++ b/nepomuk/services/storage/lib/createresourcejob.h
@@ -0,0 +1,85 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CREATERESOURCEJOB_H
+#define CREATERESOURCEJOB_H
+
+#include <KJob>
+
+#include <QtCore/QHash>
+#include <QtCore/QUrl>
+
+#include "nepomukdatamanagement_export.h"
+
+class KComponentData;
+class QDBusPendingCallWatcher;
+
+namespace Nepomuk {
+/**
+ * \class CreateResourceJob createresourcejob.h Nepomuk/CreateResourceJob
+ *
+ * \brief Job returned by Nepomuk::createResource().
+ *
+ * Access the result through the resources() method in the slot connected
+ * to the KJOb::result() signal.
+ *
+ * \author Sebastian Trueg <trueg@kde.org>
+ */
+class NEPOMUK_DATA_MANAGEMENT_EXPORT CreateResourceJob : public KJob
+{
+    Q_OBJECT
+
+public:
+    /**
+     * Destructor. The job does delete itself as soon
+     * as it is done.
+     */
+    ~CreateResourceJob();
+
+    /**
+     * The returned resource URI.
+     *
+     * Access the result in a slot connected to the KJob::result()
+     * signal.
+     */
+    QUrl resourceUri() const;
+
+private Q_SLOTS:
+    void slotDBusCallFinished(QDBusPendingCallWatcher *watcher);
+
+private:
+    CreateResourceJob(const QList<QUrl>& types,
+                      const QString& label,
+                      const QString& description,
+                      const KComponentData& component);
+    void start();
+
+    class Private;
+    Private* const d;
+
+    friend Nepomuk::CreateResourceJob* Nepomuk::createResource(const QList<QUrl>&,
+                                                               const QString&,
+                                                               const QString&,
+                                                               const KComponentData&);
+};
+}
+
+#endif
diff --git a/nepomuk/services/storage/lib/datamanagement.cpp b/nepomuk/services/storage/lib/datamanagement.cpp
new file mode 100644
index 0000000..9c570b0
--- /dev/null
+++ b/nepomuk/services/storage/lib/datamanagement.cpp
@@ -0,0 +1,169 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "datamanagement.h"
+#include "genericdatamanagementjob_p.h"
+#include "createresourcejob.h"
+#include "describeresourcesjob.h"
+#include "dbustypes.h"
+#include "simpleresourcegraph.h"
+
+#include <QtCore/QStringList>
+#include <QtCore/QMutableListIterator>
+
+#include <KUrl>
+
+
+KJob* Nepomuk::addProperty(const QList<QUrl>& resources,
+                           const QUrl& property,
+                           const QVariantList& values,
+                           const KComponentData& component)
+{
+    return new GenericDataManagementJob("addProperty",
+                                        Q_ARG(QStringList, Nepomuk::DBus::convertUriList(resources)),
+                                        Q_ARG(QString, DBus::convertUri(property)),
+                                        Q_ARG(QVariantList, Nepomuk::DBus::normalizeVariantList(values)),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+KJob* Nepomuk::setProperty(const QList<QUrl>& resources,
+                           const QUrl& property,
+                           const QVariantList& values,
+                           const KComponentData& component)
+{
+    return new GenericDataManagementJob("setProperty",
+                                        Q_ARG(QStringList, Nepomuk::DBus::convertUriList(resources)),
+                                        Q_ARG(QString, DBus::convertUri(property)),
+                                        Q_ARG(QVariantList, Nepomuk::DBus::normalizeVariantList(values)),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+
+KJob* Nepomuk::removeProperty(const QList<QUrl>& resources,
+                              const QUrl& property,
+                              const QVariantList& values,
+                              const KComponentData& component)
+{
+    return new GenericDataManagementJob("removeProperty",
+                                        Q_ARG(QStringList, Nepomuk::DBus::convertUriList(resources)),
+                                        Q_ARG(QString, DBus::convertUri(property)),
+                                        Q_ARG(QVariantList, Nepomuk::DBus::normalizeVariantList(values)),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+
+KJob* Nepomuk::removeProperties(const QList<QUrl>& resources,
+                                const QList<QUrl>& properties,
+                                const KComponentData& component)
+{
+    return new GenericDataManagementJob("removeProperties",
+                                        Q_ARG(QStringList, Nepomuk::DBus::convertUriList(resources)),
+                                        Q_ARG(QStringList, Nepomuk::DBus::convertUriList(properties)),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+
+Nepomuk::CreateResourceJob* Nepomuk::createResource(const QList<QUrl>& types,
+                                                    const QString& label,
+                                                    const QString& description,
+                                                    const KComponentData& component)
+{
+    return new CreateResourceJob(types, label, description, component);
+}
+
+
+KJob* Nepomuk::removeResources(const QList<QUrl>& resources,
+                               RemovalFlags flags,
+                               const KComponentData& component)
+{
+    return new GenericDataManagementJob("removeResources",
+                                        Q_ARG(QStringList, Nepomuk::DBus::convertUriList(resources)),
+                                        Q_ARG(int, int(flags)),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+
+KJob* Nepomuk::removeDataByApplication(const QList<QUrl>& resources,
+                                       RemovalFlags flags,
+                                       const KComponentData& component)
+{
+    return new GenericDataManagementJob("removeDataByApplication",
+                                        Q_ARG(QStringList, Nepomuk::DBus::convertUriList(resources)),
+                                        Q_ARG(int, int(flags)),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+
+KJob* Nepomuk::removeDataByApplication(RemovalFlags flags,
+                                       const KComponentData& component)
+{
+    return new GenericDataManagementJob("removeDataByApplication",
+                                        Q_ARG(int, int(flags)),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+
+KJob* Nepomuk::mergeResources(const QUrl& resource1,
+                              const QUrl& resource2,
+                              const KComponentData& component)
+{
+    return new GenericDataManagementJob("mergeResources",
+                                        Q_ARG(QString, Nepomuk::DBus::convertUri(resource1)),
+                                        Q_ARG(QString, Nepomuk::DBus::convertUri(resource2)),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+KJob* Nepomuk::storeResources(const SimpleResourceGraph& resources,
+                              StoreIdentificationMode identificationMode,
+                              StoreResourcesFlags flags,
+                              const QHash<QUrl, QVariant>& additionalMetadata,
+                              const KComponentData& component)
+{
+    return new GenericDataManagementJob("storeResources",
+                                        Q_ARG(QList<Nepomuk::SimpleResource>, resources.toList()),
+                                        Q_ARG(int, int(identificationMode)),
+                                        Q_ARG(int, int(flags)),
+                                        Q_ARG(Nepomuk::PropertyHash, additionalMetadata),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+KJob* Nepomuk::importResources(const KUrl& url,
+                               Soprano::RdfSerialization serialization,
+                               const QString& userSerialization,
+                               StoreIdentificationMode identificationMode,
+                               StoreResourcesFlags flags,
+                               const QHash<QUrl, QVariant>& additionalMetadata,
+                               const KComponentData& component)
+{
+    return new GenericDataManagementJob("importResources",
+                                        Q_ARG(QString, Nepomuk::DBus::convertUri(url)),
+                                        Q_ARG(QString, Soprano::serializationMimeType(serialization, userSerialization)),
+                                        Q_ARG(int, int(identificationMode)),
+                                        Q_ARG(int, int(flags)),
+                                        Q_ARG(Nepomuk::PropertyHash, additionalMetadata),
+                                        Q_ARG(QString, component.componentName()));
+}
+
+Nepomuk::DescribeResourcesJob* Nepomuk::describeResources(const QList<QUrl>& resources,
+                                                          bool includeSubResources)
+{
+    return new DescribeResourcesJob(resources, includeSubResources);
+}
diff --git a/nepomuk/services/storage/lib/datamanagement.h b/nepomuk/services/storage/lib/datamanagement.h
new file mode 100644
index 0000000..97f0a9b
--- /dev/null
+++ b/nepomuk/services/storage/lib/datamanagement.h
@@ -0,0 +1,466 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DATAMANAGEMENT_H
+#define DATAMANAGEMENT_H
+
+#include <QtCore/QObject>
+
+#include <KComponentData>
+#include <KGlobal>
+
+#include <Soprano/Global>
+
+#include "nepomukdatamanagement_export.h"
+
+class KJob;
+class KUrl;
+
+
+// FIXME: remove this section in KDE 4.8
+/**
+ * \mainpage %Nepomuk Data Managment API Documentation
+ *
+ * This API is subject to change!
+ *
+ * See \ref nepomuk_datamanagement for all the gory details.
+ */
+
+
+namespace Nepomuk {
+    class DescribeResourcesJob;
+    class CreateResourceJob;
+    class SimpleResourceGraph;
+
+    /**
+     * \defgroup nepomuk_datamanagement Nepomuk Data Management
+     *
+     * \brief The basic Nepomuk data manipulation interface.
+     *
+     * The Data Management API is the basic interface to manipulate data in Nepomuk. It provides methods to add, remove,
+     * change properties, add and remove resources, merge resources, integrate blobs of information, and retrieve resources
+     * according to certain criteria.
+     *
+     * The data management API can be split into two groups: the basic and the advanced API. The basic API allows to modify
+     * properties and resources in a very straight-forward manner: add or set a property value, remove property values or
+     * whole properties, and so on. The advanced API on the other hand deals with more complicated situations mostly based on
+     * the fact that Nepomuk always remembers which application did what. Each of the methods has a parameter which states
+     * the calling component. This allows clients to for example only remove information that it actually created, making
+     * updates very easy.
+     *
+     *
+     * \section nepomuk_dms_resource_uris Resource URIs
+     *
+     * Most methods take a single or a list of resource URIs. Normally all resources in Nepomuk have a unique URI of the form
+     * \c nepomuk:/res/UUID where \a UUID is a randomly generated universal identifer. However, the data management service
+     * methods can handle a bit more than that. Any local file URL can be used where a resource URI is required. It will
+     * automatically be converted to the appropriate resource URI. The same is true for any URL with a protocol supported
+     * by KIO. These URLs will not be used as resource URIs but as values of the \c nie:url property.
+     *
+     * Thus, when setting a property on a local file URL like <tt>file:///home/user/file.txt</tt> the property will actually be set
+     * on the resource corresponding to the local file. This resources has a resource URI of the form detailed above. Its
+     * \c nie:url property, however, will be set to <tt>file:///home/user/file.txt</tt>.
+     *
+     * In addition to the above URL conversion local file paths are also supported. They will be converted to local file URLs
+     * and then treated the same way as explained above.
+     *
+     * In general one should never create resource URIs manually. Instead use createResource() or storeResources() with an
+     * empty URI in the SimpleResource. The only exception are resources that have counterparts with URLs on the desktop like
+     * local files or web pages or the like.
+     *
+     *
+     * \section nepomuk_dms_sub_resources Sub-Resources
+     *
+     * Nepomuk has the concept of sub-resources. The basic idea of a sub-resource is that while it is a resource in itself, it
+     * does not make sense without its parent resource. The typical example would be contact details:
+     *
+     * \code
+     * <nepomuk:/res/A> a nco:Contact ;
+     *        nco:fullname "Nepomuk Nasenbär" ;
+     *        nco:hasPostalAddress <nepomuk:/res/B> ;
+     *        nao:hasSubResource <nepomuk:/res/B> .
+     *
+     * <nepomuk:/res/B> a nco:PostalAddress ;
+     *        nco:streetAddress "Nepomuk street 1" ;
+     *        [...] .
+     * \endcode
+     *
+     * While in theory a \c nco:PostalAddress resource could live on its own it does not make much sense without the contact.
+     * Thus, it is marked as being the sub-resource of the \c nco:Contact.
+     *
+     * Less obvious examples are contacts that are just created for indexing email senders or for indexing music files. These
+     * are contacts the user most likely does not have need for without the original data - the email or the music file. Thus,
+     * these contacts would also be marked as sub-resources of the email or music file.
+     *
+     * This allows for nice cleanup when removing resources. The methods removeResources() and removeDataByApplication() allow
+     * to specify additional flags. One of these flags is RemoveSubResources. Specifying the flag results in the removal of
+     * sub-resources.
+     *
+     *
+     * \section nepomuk_dms_metadata Resource Metadata
+     *
+     * When thinking in Nepomuk terms all the information that is added is only data, not meta-data. (The file indexer which
+     * reads file meta-data to store it in Nepomuk also creates just data.) However, Nepomuk also maintains its own meta-data.
+     * For each resource the following properties are kept as meta-data:
+     *
+     * - \c nao:created - The creation date of the resource.
+     * - \c nao:lastModified - The last time the resource was modified. This includes the addition, removal, the change of
+     * properties, both with the resource as subject and as object.
+     * - \c nao:userVisible - A boolean property stating whether the resource should be shown to the user or not. This mostly
+     * applies to graphical user interfaces and is used automatically in the Nepomuk Query API.
+     *
+     * This information is updated automatically and cannot be changed through the API (except for special cases used for
+     * syncing). But it can be queried at any time to be used for whatever purpose.
+     *
+     *
+     * \section nepomuk_dms_advanced Advanced Nepomuk Concepts
+     *
+     * This section described advanced concepts in Nepomuk such as the data layout used throughout the database.
+     *
+     * \subsection nepomuk_dms_graphs Named Graphs in Nepomuk
+     *
+     * Nepomuk makes heavy use of named graphs or contexts when talking in terms of Soprano. Essentially the named graphs allow
+     * to group triples by adding a fourth node to a <a href="http://soprano.sourceforge.net/apidox/trunk/classSoprano_1_1Statement.html">statement</a>
+     * which can then be used like any other resource. In Nepomuk this mechanism is used to categorize triples
+     * and to store meta-data about them.
+     *
+     * Nepomuk makes the distincion between four basic types of graphs:
+     * - \c nrl:Ontology - An ontology graph contains class and property definitions which are read by the ontology loader. Typically
+     * these ontologies come from the <a href="http://oscaf.sf.net/">Shared-Desktop-Ontologies</a> package installed on the system.
+     * - \c nrl:InstanceBase - The instance base is the "normal" graph type. All information added to Nepomuk by clients is stored in
+     * instance base graphs.
+     * - \c nrl:DiscardableInstanceBase - Being a sub-class of \c nrl:InstanceBase the discardable instance base also contains "normal"
+     * information. The only difference is that this information can be recreated at any time. Thus, it is seen as \a discardable and
+     * is, for example not taken into account in backups. Typical discardable information includes file meta-data created by the file indexer.
+     * - \c nrl:GraphMetadata - The graph meta-data graphs only contains meta-data about graphs. This includes the type of a graph, its
+     * creation date, and so on.
+     *
+     * In addition to its type the following information is stored about each graph, and thus, about each triple in the graph:
+     * - \c nao:created - The creation date of the graph (and the triples within)
+     * - \c nao:maintainedBy - The application which triggered the creation of the graph. This is important for methods like removeDataByApplication().
+     *
+     * Typically the information about one resource is scatterned over several graphs over time since every change to a resource leads
+     * to the creation of a new graph to save the creation date and the creating application.
+     *
+     * The following example shows the result of indexing one local file:
+     *
+     * \code
+     * <nepomuk:/ctx/b17ee4b5-ab8b-4fc5-bcc2-4bc25859cfa6> {
+     *   <nepomuk:/res/e02fe67e-7b69-4ea7-847e-ebe2d3623d5c>
+     *     nao:created “2011-05-20T11:23:45Z”^^xsd:dateTime ;
+     *     nao:lastModified “2011-05-20T11:23:45Z”^^xsd:dateTime ;
+     *
+     *     nie:contentSize "1286"^^xsd:int ;
+     *     nie:isPartOf <nepomuk:/res/80b4187c-9c40-4e98-9322-9ebcc10bd0bd> ;
+     *     nie:lastModified "2010-12-14T14:49:49Z"^^xsd:dateTime ;
+     *     nie:mimeType "text/plain"^^xsd:string ;
+     *     nie:plainTextContent "[...]"^^xsd:string ;
+     *     nie:url <file:///home/nepomuk/helloworld.txt> ;
+     *     nfo:characterCount "1249"^^xsd:int ;
+     *     nfo:fileName "helloworld.txt"^^xsd:string ;
+     *     nfo:lineCount "37"^^xsd:int ;
+     *     nfo:wordCount "126"^^xsd:int ;
+     *     a nfo:PlainTextDocument, nfo:FileDataObject .
+     * }
+     * <nepomuk:/ctx/5cf7070e-e4f4-4a0f-8b9a-fe9d94187d82> {
+     *   <nepomuk:/ctx/5cf7070e-e4f4-4a0f-8b9a-fe9d94187d82>
+     *     a nrl:GraphMetadata ;
+     *     nrl:coreGraphMetadataFor <nepomuk:/ctx/b17ee4b5-ab8b-4fc5-bcc2-4bc25859cfa6> .
+     *
+     *   <nepomuk:/ctx/b17ee4b5-ab8b-4fc5-bcc2-4bc25859cfa6>
+     *     a nrl:DiscardableInstanceBase ;
+     *     nao:created "2011-05-04T09:46:11.724Z"^^xsd:dateTime ;
+     *     nao:maintainedBy <nepomuk:/res/someapp> .
+     * }
+     * \endcode
+     *
+     * Here one important thing is to be noted: the example contains two different last modification dates: \c nao:lastModified and \c nie:lastModified.
+     * \c nie:lastModified refers to the file on disk while \c nao:lastModified refers to the Nepomuk resource in the database.
+     *
+     * \author Sebastian Trueg <trueg@kde.org>, Vishesh Handa <handa.vish@gmail.com>
+     */
+    //@{
+    /**
+     * \name Basic Data Managment API
+     */
+    //@{
+    /**
+     * \brief Flags to influence the behaviour of the data management methods
+     * removeResources() and removeDataByApplication().
+     */
+    enum RemovalFlag {
+        /// No flags - default behaviour
+        NoRemovalFlags = 0,
+
+        // trueg: why don't we make the removal of sub-resources the default?
+        /**
+         * Remove sub resources of the resources specified in the parameters.
+         * This will remove sub-resources that are not referenced by any resource
+         * that will not be deleted.
+         * See \ref nepomuk_dms_sub_resources for details.
+         */
+        RemoveSubResoures = 1
+    };
+    Q_DECLARE_FLAGS(RemovalFlags, RemovalFlag)
+
+    /**
+     * \brief Add one or more property values to one or more resources.
+     *
+     * Adds property values in addition to the existing ones.
+     *
+     * \param resources The resources to add the new property values to. See \ref nepomuk_dms_resource_uris for details.
+     * \param property The property to be changed. This needs to be the URI of a known property. If the property has
+     * cardinality restrictions which would be violated by this operation it will fail.
+     * \param values The values to add. For each resource and each value a triple will be created.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* addProperty(const QList<QUrl>& resources,
+                                                     const QUrl& property,
+                                                     const QVariantList& values,
+                                                     const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Set the values of a property for one or more resources.
+     *
+     * Sets property values overwriting/replacing the existing ones.
+     *
+     * \param resources The resources to add the new property values to. See \ref nepomuk_dms_resource_uris for details.
+     * \param property The property to be set. This needs to be the URI of a known property. If the property has
+     * cardinality restrictions which would be violated by this operation it will fail.
+     * \param values The values to set. For each resource and each value a triple will be created. Existing values will
+     * be overwritten.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* setProperty(const QList<QUrl>& resources,
+                                                     const QUrl& property,
+                                                     const QVariantList& values,
+                                                     const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Remove values of a property from one or more resources.
+     *
+     * Removes the given property values.
+     *
+     * \param resources The resources to remove the property values from. See \ref nepomuk_dms_resource_uris for details.
+     * \param property The property to be changed. This needs to be the URI of a known property. If the property has
+     * cardinality restrictions which would be violated by this operation it will fail.
+     * \param values The values to remove.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* removeProperty(const QList<QUrl>& resources,
+                                                        const QUrl& property,
+                                                        const QVariantList& values,
+                                                        const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Remove one or more properties from one or more resources.
+     *
+     * Removes all values from all given properties from all given resources.
+     *
+     * \param resources The resources to remove the properties from. See \ref nepomuk_dms_resource_uris for details.
+     * \param properties The properties to be changed. These need to be the URIs of known properties. If one pf the
+     * properties has cardinality restrictions which would be violated by this operation it will fail.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* removeProperties(const QList<QUrl>& resources,
+                                                          const QList<QUrl>& properties,
+                                                          const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Create a new resource.
+     *
+     * Creates a new resource with the given types, label, and description and returns the new resource's URI.
+     *
+     * \param types A list of RDF types that the new resource should have. These need to be the URIs of known RDF classes.
+     * \param label The optional nao:prefLabel to be set.
+     * \param description The optional nao:description to be set.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT CreateResourceJob* createResource(const QList<QUrl>& types,
+                                                                     const QString& label,
+                                                                     const QString& description,
+                                                                     const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Completely remove resources from the database.
+     *
+     * \param resources The resources to remove. See \ref nepomuk_dms_resource_uris for details.
+     * \param flags Optional flags to change the detail of what should be removed.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* removeResources(const QList<QUrl>& resources,
+                                                         Nepomuk::RemovalFlags flags = Nepomuk::NoRemovalFlags,
+                                                         const KComponentData& component = KGlobal::mainComponent());
+    //@}
+
+    /**
+     * \name Advanced Data Managment API
+     */
+    //@{
+    /**
+     * \brief The identification mode used by storeResources().
+     *
+     * This states which given resources should be merged
+     * with existing ones that match.
+     */
+    enum StoreIdentificationMode {
+        /// This is the default mode. Only new resources without a resource URI are identified. All others
+        /// are just saved with their given URI, provided the URI already exists.
+        IdentifyNew = 0,
+
+        /// All resources are treated as new ones. The only exception are those with a defined
+        /// resource URI.
+        IdentifyNone = 2
+    };
+
+    /**
+     * \brief Flags to influence the behaviour of storeResources().
+     */
+    enum StoreResourcesFlag {
+        /// No flags - default behaviour
+        NoStoreResourcesFlags = 0,
+
+        /// By default storeResources() will only append data and fail if properties with
+        /// cardinality 1 already have a value. This flag changes the behaviour to force the
+        /// new values instead.
+        OverwriteProperties = 1,
+
+        /// When lazy cardinalities are enabled any value that would violate a cardinality restriction
+        /// is simply dropped without throwing an error.
+        LazyCardinalities = 2
+    };
+    Q_DECLARE_FLAGS(StoreResourcesFlags, StoreResourcesFlag)
+
+    /**
+     * \brief Remove all information about resources from the database which
+     * has been created by a specific application.
+     *
+     * \param resources The resources to remove the data from. See \ref nepomuk_dms_resource_uris for details.
+     * \param flags Optional flags to change the detail of what should be removed. When specifying RemoveSubResources
+     * even sub-resources created by other applications are removed if they are not referenced by other resources
+     * anymore.
+     * \param component The calling component. Only data created by this component is removed. Everything else
+     * is left untouched. Essential properties like \a nie:url are only removed if the entire resource is
+     * removed.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* removeDataByApplication(const QList<QUrl>& resources,
+                                                                 Nepomuk::RemovalFlags flags = Nepomuk::NoRemovalFlags,
+                                                                 const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Remove all information created by a specific application.
+     *
+     * \param flags Optional flags to change the detail of what should be removed. Here RemoveSubResources is a bit
+     * special as it only applies to resources that are removed completely and spans sub-resources created by
+     * other applications.
+     * \param component The calling component. Only data created by this component is removed. Everything else
+     * is left untouched. Essential properties like \a nie:url are only removed if the entire resource is
+     * removed.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* removeDataByApplication(Nepomuk::RemovalFlags flags = Nepomuk::NoRemovalFlags,
+                                                                 const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Merge two resources into one.
+     *
+     * \param resource1 The first resource to merge. If both resources have conflicting properties like different
+     * values on a property with a cardinality restriction the values from resource1 take precedence.
+     * See \ref nepomuk_dms_resource_uris for details.
+     * \param resource2 The resource to be merged into the first. See \ref nepomuk_dms_resource_uris for details.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* mergeResources(const QUrl& resource1,
+                                                        const QUrl& resource2,
+                                                        const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Store many resources at once.
+     *
+     * This is the most powerful method of them all. It allows to store a whole set of resources in one
+     * go including creating new resources.
+     *
+     * \param resources The resources to be merged. Blank nodes (URIs of the form \a _:xyz) will be converted into new
+     * URIs (unless the identificationMode allows to merge with an existing resource). See \ref nepomuk_dms_resource_uris for details.
+     * \param identificationMode This method can try hard to avoid duplicate resources by looking
+     * for already existing duplicates based on nrl:IdentifyingProperty. By default it only looks
+     * for duplicates of resources that do not have a resource URI (SimpleResource::uri()) defined.
+     * This behaviour can be changed with this parameter.
+     * \param flags Additional flags to change the behaviour of the method.
+     * \param additionalMetadata Additional metadata for the added resources. This can include
+     * such details as the creator of the data or details on the method of data recovery.
+     * One typical usecase is that the file indexer uses (rdf:type, nrl:DiscardableInstanceBase)
+     * to state that the provided information can be recreated at any time. Only built-in types
+     * such as int, string, or url are supported.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* storeResources(const Nepomuk::SimpleResourceGraph& resources,
+                                                        Nepomuk::StoreIdentificationMode identificationMode = Nepomuk::IdentifyNew,
+                                                        Nepomuk::StoreResourcesFlags flags = Nepomuk::NoStoreResourcesFlags,
+                                                        const QHash<QUrl, QVariant>& additionalMetadata = QHash<QUrl, QVariant>(),
+                                                        const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Import an RDF graph from a URL.
+     *
+     * This is essentially the same method as storeResources() except that it uses a different method
+     * encoding the resources.
+     *
+     * \param url The url from which the graph should be loaded. This does not have to be local.
+     * \param serialization The RDF serialization used for the file. If Soprano::SerializationUnknown a crude automatic
+     * detection based on file extension is used.
+     * \param userSerialization If \p serialization is Soprano::SerializationUser this value is used. See Soprano::Parser
+     * for details.
+     * \param identificationMode This method can try hard to avoid duplicate resources by looking
+     * for already existing duplicates based on nrl:IdentifyingProperty. By default it only looks
+     * for duplicates of resources that do not have a resource URI (SimpleResource::uri()) defined.
+     * This behaviour can be changed with this parameter.
+     * \param flags Additional flags to change the behaviour of the method.
+     * \param additionalMetadata Additional metadata for the added resources. This can include
+     * such details as the creator of the data or details on the method of data recovery.
+     * One typical usecase is that the file indexer uses (rdf:type, nrl:DiscardableInstanceBase)
+     * to state that the provided information can be recreated at any time. Only built-in types
+     * such as int, string, or url are supported.
+     * \param component The calling component. Typically this is left to the default.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT KJob* importResources(const KUrl& url,
+                                                         Soprano::RdfSerialization serialization,
+                                                         const QString& userSerialization = QString(),
+                                                         StoreIdentificationMode identificationMode = IdentifyNew,
+                                                         StoreResourcesFlags flags = NoStoreResourcesFlags,
+                                                         const QHash<QUrl, QVariant>& additionalMetadata = QHash<QUrl, QVariant>(),
+                                                         const KComponentData& component = KGlobal::mainComponent());
+
+    /**
+     * \brief Retrieve all information about a set of resources.
+     *
+     * \param resources The resources to describe. See \ref nepomuk_dms_resource_uris for details.
+     * \param includeSubResources If \p true sub resources will be included. See \ref nepomuk_dms_sub_resources for details.
+     */
+    NEPOMUK_DATA_MANAGEMENT_EXPORT DescribeResourcesJob* describeResources(const QList<QUrl>& resources,
+                                                                           bool includeSubResources);
+    //@}
+    //@}
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Nepomuk::RemovalFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Nepomuk::StoreResourcesFlags)
+
+#endif
diff --git a/nepomuk/services/storage/lib/datamanagementinterface.cpp b/nepomuk/services/storage/lib/datamanagementinterface.cpp
new file mode 100644
index 0000000..5019353
--- /dev/null
+++ b/nepomuk/services/storage/lib/datamanagementinterface.cpp
@@ -0,0 +1,28 @@
+/*
+ * This file was generated by qdbusxml2cpp version 0.7
+ * Command line was: qdbusxml2cpp -m -i dbustypes.h -p datamanagementinterface /home/trueg/kde/dev/kde/src/kde-runtime/nepomuk/interfaces/org.kde.nepomuk.DataManagement.xml
+ *
+ * qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This is an auto-generated file.
+ * This file may have been hand-edited. Look for HAND-EDIT comments
+ * before re-generating it.
+ */
+
+#include "datamanagementinterface.h"
+
+/*
+ * Implementation of interface class OrgKdeNepomukDataManagementInterface
+ */
+
+OrgKdeNepomukDataManagementInterface::OrgKdeNepomukDataManagementInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
+    : AbstractTimeoutDBusInterface(service, path, staticInterfaceName(), connection, parent)
+{
+}
+
+OrgKdeNepomukDataManagementInterface::~OrgKdeNepomukDataManagementInterface()
+{
+}
+
+
+#include "datamanagementinterface.moc"
diff --git a/nepomuk/services/storage/lib/datamanagementinterface.h b/nepomuk/services/storage/lib/datamanagementinterface.h
new file mode 100644
index 0000000..bf51780
--- /dev/null
+++ b/nepomuk/services/storage/lib/datamanagementinterface.h
@@ -0,0 +1,190 @@
+/*
+ * This file was generated by qdbusxml2cpp version 0.7
+ * Command line was: qdbusxml2cpp -m -i dbustypes.h -p datamanagementinterface /home/trueg/kde/dev/kde/src/kde-runtime/nepomuk/interfaces/org.kde.nepomuk.DataManagement.xml
+ *
+ * qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This is an auto-generated file.
+ *
+ * It contains one small change: it is derived from AbstractTimeoutDbusInterface instead of QDBusAbstractInterface and uses a big timeout.
+ */
+
+#ifndef DATAMANAGEMENTINTERFACE_H_1308595912
+#define DATAMANAGEMENTINTERFACE_H_1308595912
+
+#include <QtCore/QObject>
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+#include <QtDBus/QtDBus>
+#include "dbustypes.h"
+
+#include "abstracttimeoutdbusinterface.h"
+
+/*
+ * Proxy class for interface org.kde.nepomuk.DataManagement
+ */
+class OrgKdeNepomukDataManagementInterface: public AbstractTimeoutDBusInterface
+{
+    Q_OBJECT
+
+    /// we use a big timeout (10 min) since commands are queued in the DMS
+    static const int s_defaultTimeout = 600000;
+
+public:
+    static inline const char *staticInterfaceName()
+    { return "org.kde.nepomuk.DataManagement"; }
+
+public:
+    OrgKdeNepomukDataManagementInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);
+
+    ~OrgKdeNepomukDataManagementInterface();
+
+public Q_SLOTS: // METHODS
+    inline QDBusPendingReply<> addProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resource) << qVariantFromValue(property) << qVariantFromValue(value) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("addProperty"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> addProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resources) << qVariantFromValue(property) << qVariantFromValue(values) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("addProperty"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<QString> createResource(const QString &type, const QString &label, const QString &description, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(type) << qVariantFromValue(label) << qVariantFromValue(description) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("createResource"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<QString> createResource(const QStringList &types, const QString &label, const QString &description, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(types) << qVariantFromValue(label) << qVariantFromValue(description) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("createResource"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<QList<Nepomuk::SimpleResource> > describeResources(const QStringList &resources, bool includeSubResources)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resources) << qVariantFromValue(includeSubResources);
+        return asyncCallWithArgumentList(QLatin1String("describeResources"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> importResources(const QString &url, const QString &serialization, int identificationMode, int flags, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(url) << qVariantFromValue(serialization) << qVariantFromValue(identificationMode) << qVariantFromValue(flags) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("importResources"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> importResources(const QString &url, const QString &serialization, int identificationMode, int flags, Nepomuk::PropertyHash additionalMetadata, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(url) << qVariantFromValue(serialization) << qVariantFromValue(identificationMode) << qVariantFromValue(flags) << qVariantFromValue(additionalMetadata) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("importResources"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> mergeResources(const QString &resource1, const QString &resource2, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resource1) << qVariantFromValue(resource2) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("mergeResources"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> removeDataByApplication(int flags, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(flags) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("removeDataByApplication"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> removeDataByApplication(const QStringList &resources, int flags, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resources) << qVariantFromValue(flags) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("removeDataByApplication"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> removeProperties(const QString &resource, const QString &property, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resource) << qVariantFromValue(property) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("removeProperties"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> removeProperties(const QStringList &resources, const QStringList &properties, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resources) << qVariantFromValue(properties) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("removeProperties"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> removeProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resource) << qVariantFromValue(property) << qVariantFromValue(value) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("removeProperty"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> removeProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resources) << qVariantFromValue(property) << qVariantFromValue(values) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("removeProperty"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> removeResources(const QString &resource, int flags, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resource) << qVariantFromValue(flags) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("removeResources"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> removeResources(const QStringList &resources, int flags, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resources) << qVariantFromValue(flags) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("removeResources"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> setProperty(const QString &resource, const QString &property, const QDBusVariant &value, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resource) << qVariantFromValue(property) << qVariantFromValue(value) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("setProperty"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> setProperty(const QStringList &resources, const QString &property, const QVariantList &values, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resources) << qVariantFromValue(property) << qVariantFromValue(values) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("setProperty"), argumentList, s_defaultTimeout);
+    }
+
+    inline QDBusPendingReply<> storeResources(const QList<Nepomuk::SimpleResource> &resources, int identificationMode, int flags, Nepomuk::PropertyHash additionalMetadata, const QString &app)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(resources) << qVariantFromValue(identificationMode) << qVariantFromValue(flags) << qVariantFromValue(additionalMetadata) << qVariantFromValue(app);
+        return asyncCallWithArgumentList(QLatin1String("storeResources"), argumentList, s_defaultTimeout);
+    }
+
+Q_SIGNALS: // SIGNALS
+};
+
+namespace org {
+  namespace kde {
+    namespace nepomuk {
+      typedef ::OrgKdeNepomukDataManagementInterface DataManagement;
+    }
+  }
+}
+#endif
diff --git a/nepomuk/services/storage/lib/dbustypes.cpp b/nepomuk/services/storage/lib/dbustypes.cpp
new file mode 100644
index 0000000..27fd0f4
--- /dev/null
+++ b/nepomuk/services/storage/lib/dbustypes.cpp
@@ -0,0 +1,208 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dbustypes.h"
+
+#include <QtCore/QStringList>
+#include <QtCore/QDate>
+#include <QtCore/QTime>
+#include <QtCore/QDateTime>
+#include <QtDBus/QDBusMetaType>
+
+#include <KUrl>
+#include <KDebug>
+
+QString Nepomuk::DBus::convertUri(const QUrl& uri)
+{
+    return KUrl(uri).url();
+}
+
+QStringList Nepomuk::DBus::convertUriList(const QList<QUrl>& uris)
+{
+    QStringList uriStrings;
+    foreach(const QUrl& uri, uris)
+        uriStrings << convertUri(uri);
+    return uriStrings;
+}
+
+QVariantList Nepomuk::DBus::normalizeVariantList(const QVariantList& l)
+{
+    QVariantList newL;
+    QListIterator<QVariant> it(l);
+    while(it.hasNext()) {
+        QVariant v = it.next();
+        if(v.userType() == qMetaTypeId<KUrl>()) {
+            newL.append(QVariant(QUrl(v.value<KUrl>())));
+        }
+        else {
+            newL.append(v);
+        }
+    }
+    return newL;
+}
+
+QVariant Nepomuk::DBus::resolveDBusArguments(const QVariant& v)
+{
+    //
+    // trueg: QDBus does not automatically convert non-basic types but gives us a QDBusArgument in a QVariant.
+    // Thus, we need to handle QUrl, QTime, QDate, and QDateTime as a special cases here. They is the only complex types we support.
+    //
+    if(v.userType() == qMetaTypeId<QDBusArgument>()) {
+        const QDBusArgument arg = v.value<QDBusArgument>();
+
+        QVariant v;
+        if(arg.currentSignature() == QLatin1String("(s)")) {
+            QUrl url;
+            arg >> url;
+            return url;
+        }
+        else if(arg.currentSignature() == QLatin1String("(iii)")) {
+            QDate date;
+            arg >> date;
+            return date;
+        }
+        else if(arg.currentSignature() == QLatin1String("(iiii)")) {
+            QTime time;
+            arg >> time;
+            return time;
+        }
+        else if(arg.currentSignature() == QLatin1String("((iii)(iiii)i)")) {
+            QDateTime dt;
+            arg >> dt;
+            return dt;
+        }
+        else {
+            kDebug() << "Unknown type signature in property hash value:" << arg.currentSignature();
+            return QVariant();
+        }
+    }
+    else {
+        return v;
+    }
+}
+
+QVariantList Nepomuk::DBus::resolveDBusArguments(const QVariantList& l)
+{
+    QVariantList newL;
+    QListIterator<QVariant> it(l);
+    while(it.hasNext()) {
+        newL.append(resolveDBusArguments(it.next()));
+    }
+    return newL;
+}
+
+void Nepomuk::DBus::registerDBusTypes()
+{
+    // we need QUrl to be able to pass it in a QVariant
+    qDBusRegisterMetaType<QUrl>();
+
+    // the central struct for storeResources and describeResources
+    qDBusRegisterMetaType<Nepomuk::SimpleResource>();
+
+    // we use a list instead of a struct for SimpleResourceGraph
+    qDBusRegisterMetaType<QList<Nepomuk::SimpleResource> >();
+
+    // required for the additional metadata in storeResources
+    qDBusRegisterMetaType<Nepomuk::PropertyHash>();
+}
+
+// We need the QUrl serialization to be able to pass URIs in variants
+QDBusArgument& operator<<( QDBusArgument& arg, const QUrl& url )
+{
+    arg.beginStructure();
+    arg << QString::fromAscii(url.toEncoded());
+    arg.endStructure();
+    return arg;
+}
+
+// We need the QUrl serialization to be able to pass URIs in variants
+const QDBusArgument& operator>>( const QDBusArgument& arg, QUrl& url )
+{
+    arg.beginStructure();
+    QString uriString;
+    arg >> uriString;
+    url = QUrl::fromEncoded(uriString.toAscii());
+    arg.endStructure();
+    return arg;
+}
+
+QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::PropertyHash& ph )
+{
+    arg.beginMap( QVariant::String, qMetaTypeId<QDBusVariant>());
+    for(Nepomuk::PropertyHash::const_iterator it = ph.constBegin();
+        it != ph.constEnd(); ++it) {
+        arg.beginMapEntry();
+        arg << QString::fromAscii(it.key().toEncoded());
+
+        // a small hack to allow usage of KUrl
+        if(it.value().userType() == qMetaTypeId<KUrl>())
+            arg << QDBusVariant(QUrl(it.value().value<KUrl>()));
+        else
+            arg << QDBusVariant(it.value());
+
+        arg.endMapEntry();
+    }
+    arg.endMap();
+    return arg;
+}
+
+const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::PropertyHash& ph )
+{
+    ph.clear();
+    arg.beginMap();
+    while(!arg.atEnd()) {
+        QString key;
+        QDBusVariant value;
+        arg.beginMapEntry();
+        arg >> key >> value;
+
+        const QUrl p = QUrl::fromEncoded(key.toAscii());
+        const QVariant v = Nepomuk::DBus::resolveDBusArguments(value.variant());
+
+        ph.insertMulti(p, v);
+
+        arg.endMapEntry();
+    }
+    arg.endMap();
+    return arg;
+}
+
+QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::SimpleResource& res )
+{
+    arg.beginStructure();
+    arg << QString::fromAscii(res.uri().toEncoded());
+    arg << res.properties();
+    arg.endStructure();
+    return arg;
+}
+
+const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::SimpleResource& res )
+{
+    arg.beginStructure();
+    QString uriS;
+    Nepomuk::PropertyHash props;
+    arg >> uriS;
+    res.setUri( QUrl::fromEncoded(uriS.toAscii()) );
+    arg >> props;
+    res.setProperties(props);
+    arg.endStructure();
+    return arg;
+}
diff --git a/nepomuk/services/storage/lib/dbustypes.h b/nepomuk/services/storage/lib/dbustypes.h
new file mode 100644
index 0000000..900e219
--- /dev/null
+++ b/nepomuk/services/storage/lib/dbustypes.h
@@ -0,0 +1,62 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DBUSTYPES_H
+#define DBUSTYPES_H
+
+#include <QtCore/QMetaType>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+#include <QtCore/QUrl>
+#include <QtDBus/QDBusVariant>
+#include <QtDBus/QDBusArgument>
+
+#include "simpleresource.h"
+#include "nepomukdatamanagement_export.h"
+
+Q_DECLARE_METATYPE(Nepomuk::PropertyHash)
+Q_DECLARE_METATYPE(Nepomuk::SimpleResource)
+Q_DECLARE_METATYPE(QList<Nepomuk::SimpleResource>)
+
+namespace Nepomuk {
+    namespace DBus {
+        QString convertUri(const QUrl& uri);
+        QStringList convertUriList(const QList<QUrl>& uris);
+
+        /// Convert QDBusArguments variants into QUrl, QDate, QTime, and QDateTime variants
+        NEPOMUK_DATA_MANAGEMENT_EXPORT QVariant resolveDBusArguments(const QVariant& v);
+        NEPOMUK_DATA_MANAGEMENT_EXPORT QVariantList resolveDBusArguments(const QVariantList& l);
+
+        /// Replaces KUrl with QUrl for DBus marshalling.
+        QVariantList normalizeVariantList(const QVariantList& l);
+
+        NEPOMUK_DATA_MANAGEMENT_EXPORT void registerDBusTypes();
+    }
+}
+
+QDBusArgument& operator<<( QDBusArgument& arg, const QUrl& url );
+const QDBusArgument& operator>>( const QDBusArgument& arg, QUrl& url );
+QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::PropertyHash& ph );
+const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::PropertyHash& ph );
+QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::SimpleResource& res );
+const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::SimpleResource& res );
+
+#endif // DBUSTYPES_H
diff --git a/nepomuk/services/storage/lib/describeresourcesjob.cpp b/nepomuk/services/storage/lib/describeresourcesjob.cpp
new file mode 100644
index 0000000..b955ea1
--- /dev/null
+++ b/nepomuk/services/storage/lib/describeresourcesjob.cpp
@@ -0,0 +1,92 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "describeresourcesjob.h"
+#include "datamanagementinterface.h"
+#include "simpleresourcegraph.h"
+#include "dbustypes.h"
+#include "genericdatamanagementjob_p.h"
+
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusPendingReply>
+#include <QtDBus/QDBusPendingCallWatcher>
+
+#include <QtCore/QVariant>
+#include <QtCore/QUrl>
+
+#include <KComponentData>
+#include <KDebug>
+
+
+class Nepomuk::DescribeResourcesJob::Private
+{
+public:
+    SimpleResourceGraph m_resources;
+};
+
+Nepomuk::DescribeResourcesJob::DescribeResourcesJob(const QList<QUrl>& resources,
+                                                    bool includeSubResources)
+    : KJob(0),
+      d(new Private)
+{
+    DBus::registerDBusTypes();
+
+    org::kde::nepomuk::DataManagement dms(QLatin1String(DMS_DBUS_SERVICE),
+                                          QLatin1String("/datamanagement"),
+                                          QDBusConnection::sessionBus());
+    QDBusPendingCallWatcher* dbusCallWatcher
+            = new QDBusPendingCallWatcher(dms.describeResources(Nepomuk::DBus::convertUriList(resources),
+                                                                includeSubResources));
+    connect(dbusCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+            this, SLOT(slotDBusCallFinished(QDBusPendingCallWatcher*)));
+}
+
+Nepomuk::DescribeResourcesJob::~DescribeResourcesJob()
+{
+    delete d;
+}
+
+void Nepomuk::DescribeResourcesJob::start()
+{
+    // do nothing, we do everything in the constructor
+}
+
+void Nepomuk::DescribeResourcesJob::slotDBusCallFinished(QDBusPendingCallWatcher *watcher)
+{
+    QDBusPendingReply<QList<Nepomuk::SimpleResource> > reply = *watcher;
+    if (reply.isError()) {
+        QDBusError error = reply.error();
+        setError(1);
+        setErrorText(error.message());
+    }
+    else {
+        d->m_resources = reply.value();
+    }
+    watcher->deleteLater();
+    emitResult();
+}
+
+Nepomuk::SimpleResourceGraph Nepomuk::DescribeResourcesJob::resources() const
+{
+    return d->m_resources;
+}
+
+#include "describeresourcesjob.moc"
diff --git a/nepomuk/services/storage/lib/describeresourcesjob.h b/nepomuk/services/storage/lib/describeresourcesjob.h
new file mode 100644
index 0000000..b63cd2b
--- /dev/null
+++ b/nepomuk/services/storage/lib/describeresourcesjob.h
@@ -0,0 +1,82 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DESCRIBERESOURCESJOB_H
+#define DESCRIBERESOURCESJOB_H
+
+#include <KJob>
+
+#include <QtCore/QList>
+#include <QtCore/QUrl>
+
+#include "nepomukdatamanagement_export.h"
+
+class QDBusPendingCallWatcher;
+
+namespace Nepomuk {
+class SimpleResourceGraph;
+
+/**
+ * \class DescribeResourcesJob describeresourcesjob.h Nepomuk/DescribeResourcesJob
+ *
+ * \brief Job returned by Nepomuk::describeResources().
+ *
+ * Access the result through the resources() method in the slot connected
+ * to the KJOb::result() signal.
+ *
+ * \author Sebastian Trueg <trueg@kde.org>
+ */
+class NEPOMUK_DATA_MANAGEMENT_EXPORT DescribeResourcesJob : public KJob
+{
+    Q_OBJECT
+
+public:
+    /**
+     * Destructor. The job does delete itself as soon
+     * as it is done.
+     */
+    ~DescribeResourcesJob();
+
+    /**
+     * The returned resources.
+     *
+     * Access the result in a slot connected to the KJob::result()
+     * signal.
+     */
+    SimpleResourceGraph resources() const;
+
+private Q_SLOTS:
+    void slotDBusCallFinished(QDBusPendingCallWatcher *watcher);
+
+private:
+    DescribeResourcesJob(const QList<QUrl>& resources,
+                         bool includeSubResources);
+    void start();
+
+    class Private;
+    Private* const d;
+
+    friend Nepomuk::DescribeResourcesJob* Nepomuk::describeResources(const QList<QUrl>&,
+                                                                     bool);
+};
+}
+
+#endif
diff --git a/nepomuk/services/storage/lib/genericdatamanagementjob.cpp b/nepomuk/services/storage/lib/genericdatamanagementjob.cpp
new file mode 100644
index 0000000..949dfc0
--- /dev/null
+++ b/nepomuk/services/storage/lib/genericdatamanagementjob.cpp
@@ -0,0 +1,88 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "genericdatamanagementjob_p.h"
+#include "datamanagementinterface.h"
+#include "dbustypes.h"
+
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusPendingReply>
+#include <QtDBus/QDBusPendingCallWatcher>
+
+#include <QtCore/QVariant>
+#include <QtCore/QHash>
+
+#include <KDebug>
+
+Nepomuk::GenericDataManagementJob::GenericDataManagementJob(const char *methodName,
+                                                            QGenericArgument val0,
+                                                            QGenericArgument val1,
+                                                            QGenericArgument val2,
+                                                            QGenericArgument val3,
+                                                            QGenericArgument val4,
+                                                            QGenericArgument val5)
+    : KJob(0)
+{
+    // DBus types necessary for storeResources
+    DBus::registerDBusTypes();
+
+    org::kde::nepomuk::DataManagement dms(QLatin1String(DMS_DBUS_SERVICE),
+                                          QLatin1String("/datamanagement"),
+                                          QDBusConnection::sessionBus());
+    QDBusPendingReply<> reply;
+    QMetaObject::invokeMethod(&dms,
+                              methodName,
+                              Qt::DirectConnection,
+                              Q_RETURN_ARG(QDBusPendingReply<> , reply),
+                              val0,
+                              val1,
+                              val2,
+                              val3,
+                              val4,
+                              val5);
+    QDBusPendingCallWatcher* dbusCallWatcher = new QDBusPendingCallWatcher(reply);
+    connect(dbusCallWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+            this, SLOT(slotDBusCallFinished(QDBusPendingCallWatcher*)));
+}
+
+Nepomuk::GenericDataManagementJob::~GenericDataManagementJob()
+{
+}
+
+void Nepomuk::GenericDataManagementJob::start()
+{
+    // do nothing
+}
+
+void Nepomuk::GenericDataManagementJob::slotDBusCallFinished(QDBusPendingCallWatcher *watcher)
+{
+    QDBusPendingReply<> reply = *watcher;
+    if (reply.isError()) {
+        QDBusError error = reply.error();
+        kDebug() << error;
+        setError(int(error.type()));
+        setErrorText(error.message());
+    }
+    delete watcher;
+    emitResult();
+}
+
+#include "genericdatamanagementjob_p.moc"
diff --git a/nepomuk/services/storage/lib/genericdatamanagementjob_p.h b/nepomuk/services/storage/lib/genericdatamanagementjob_p.h
new file mode 100644
index 0000000..4774ba0
--- /dev/null
+++ b/nepomuk/services/storage/lib/genericdatamanagementjob_p.h
@@ -0,0 +1,64 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef GENERICDATAMANAGEMENTJOB_H
+#define GENERICDATAMANAGEMENTJOB_H
+
+#include <QtGlobal>
+
+#ifndef NDEBUG
+#define DMS_DBUS_SERVICE (qgetenv("NEPOMUK_FAKE_DMS_DBUS_SERVICE").isEmpty() ? "org.kde.nepomuk.DataManagement" : qgetenv("NEPOMUK_FAKE_DMS_DBUS_SERVICE").constData())
+#else
+#define DMS_DBUS_SERVICE "org.kde.nepomuk.DataManagement"
+#endif
+
+#include <KJob>
+
+class QDBusPendingCallWatcher;
+
+namespace Nepomuk {
+class GenericDataManagementJob : public KJob
+{
+    Q_OBJECT
+
+public:
+    /**
+     * Start any Data Management Service method with a void
+     * return type.
+     */
+    GenericDataManagementJob(const char* methodName,
+                             QGenericArgument val0,
+                             QGenericArgument val1 = QGenericArgument(),
+                             QGenericArgument val2 = QGenericArgument(),
+                             QGenericArgument val3 = QGenericArgument(),
+                             QGenericArgument val4 = QGenericArgument(),
+                             QGenericArgument val5 = QGenericArgument());
+    ~GenericDataManagementJob();
+
+    /// does nothing, we do all in the constructor - it simply needs to be implemented
+    void start();
+
+private Q_SLOTS:
+    void slotDBusCallFinished(QDBusPendingCallWatcher*);
+};
+}
+
+#endif
diff --git a/nepomuk/services/storage/lib/nepomukdatamanagement_export.h b/nepomuk/services/storage/lib/nepomukdatamanagement_export.h
new file mode 100644
index 0000000..929a737
--- /dev/null
+++ b/nepomuk/services/storage/lib/nepomukdatamanagement_export.h
@@ -0,0 +1,40 @@
+/*  This file is part of the KDE project
+    Copyright (C) 2007 David Faure <faure@kde.org>
+
+    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 Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef NEPOMUKDATAMANAGEMENT_EXPORT_H
+#define NEPOMUKDATAMANAGEMENT_EXPORT_H
+
+/* needed for KDE_EXPORT and KDE_IMPORT macros */
+#include <kdemacros.h>
+
+#ifndef NEPOMUK_DATA_MANAGEMENT_EXPORT
+# if defined(MAKE_NEPOMUKDATAMANAGEMENT_LIB)
+   /* We are building this library */ 
+#  define NEPOMUK_DATA_MANAGEMENT_EXPORT KDE_EXPORT
+# else
+   /* We are using this library */ 
+#  define NEPOMUK_DATA_MANAGEMENT_EXPORT KDE_IMPORT
+# endif
+#endif
+
+# ifndef NEPOMUK_DATA_MANAGEMENT_EXPORT_DEPRECATED
+#  define NEPOMUK_DATA_MANAGEMENT_EXPORT_DEPRECATED KDE_DEPRECATED NEPOMUK_DATA_MANAGEMENT_EXPORT
+# endif
+
+#endif
diff --git a/nepomuk/services/storage/lib/resourcewatcher.cpp b/nepomuk/services/storage/lib/resourcewatcher.cpp
new file mode 100644
index 0000000..f6fc93c
--- /dev/null
+++ b/nepomuk/services/storage/lib/resourcewatcher.cpp
@@ -0,0 +1,203 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2011  Vishesh Handa <handa.vish@gmail.com>
+    Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+#include "resourcewatcher.h"
+#include "resourcewatcherconnectioninterface.h"
+#include "resourcewatchermanagerinterface.h"
+
+#include <QtDBus/QDBusObjectPath>
+
+#include <Nepomuk/Resource>
+
+#include <KUrl>
+
+namespace {
+QList<QUrl> convertUris(const QStringList& uris) {
+    QList<QUrl> us;
+    foreach(const QString& uri, uris) {
+        us << KUrl(uri);
+    }
+    return us;
+}
+}
+
+class Nepomuk::ResourceWatcher::Private {
+public:
+    QList<Types::Class> m_types;
+    QList<Nepomuk::Resource> m_resources;
+    QList<Types::Property> m_properties;
+
+    org::kde::nepomuk::ResourceWatcherConnection * m_connectionInterface;
+    org::kde::nepomuk::ResourceWatcher * m_watchManagerInterface;
+};
+
+Nepomuk::ResourceWatcher::ResourceWatcher(QObject* parent)
+    : QObject(parent),
+      d(new Private)
+{
+    d->m_watchManagerInterface
+            = new org::kde::nepomuk::ResourceWatcher( "org.kde.nepomuk.DataManagement",
+                                                      "/resourcewatcher",
+                                                      QDBusConnection::sessionBus() );
+    d->m_connectionInterface = 0;
+}
+
+Nepomuk::ResourceWatcher::~ResourceWatcher()
+{
+    stop();
+    delete d;
+}
+
+bool Nepomuk::ResourceWatcher::start()
+{
+    //
+    // Convert to list of strings
+    //
+    QList<QString> uris;
+    foreach( const Nepomuk::Resource & res, d->m_resources ) {
+        uris << KUrl(res.resourceUri()).url();
+    }
+
+    QList<QString> props;
+    foreach( const Types::Property & prop, d->m_properties ) {
+        props << KUrl(prop.uri()).url();
+    }
+
+    QList<QString> types_;
+    foreach( const Types::Class & cl, d->m_types ) {
+        types_ << KUrl(cl.uri()).url();
+    }
+
+    //
+    // Create the dbus object to watch
+    //
+    QDBusPendingReply<QDBusObjectPath> reply = d->m_watchManagerInterface->watch( uris, props, types_ );
+    QDBusObjectPath path = reply.value();
+
+    if(!path.path().isEmpty()) {
+        d->m_connectionInterface = new org::kde::nepomuk::ResourceWatcherConnection( "org.kde.nepomuk.DataManagement",
+                                                                                     path.path(),
+                                                                                     QDBusConnection::sessionBus() );
+        connect( d->m_connectionInterface, SIGNAL(propertyAdded(QString,QString,QDBusVariant)),
+                 this, SLOT(slotPropertyAdded(QString,QString,QDBusVariant)) );
+        connect( d->m_connectionInterface, SIGNAL(propertyRemoved(QString,QString,QDBusVariant)),
+                 this, SLOT(slotPropertyRemoved(QString,QString,QDBusVariant)) );
+        connect( d->m_connectionInterface, SIGNAL(resourceCreated(QString,QStringList)),
+                 this, SLOT(slotResourceCreated(QString,QStringList)) );
+        connect( d->m_connectionInterface, SIGNAL(resourceRemoved(QString,QStringList)),
+                 this, SLOT(slotResourceRemoved(QString,QStringList)) );
+        connect( d->m_connectionInterface, SIGNAL(resourceTypeAdded(QString,QString)),
+                 this, SLOT(slotResourceTypeAdded(QString,QString)) );
+        connect( d->m_connectionInterface, SIGNAL(resourceTypeRemoved(QString,QString)),
+                 this, SLOT(slotResourceTypeRemoved(QString,QString)) );
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+void Nepomuk::ResourceWatcher::stop()
+{
+    if (d->m_connectionInterface) {
+        d->m_connectionInterface->close();
+        delete d->m_connectionInterface;
+        d->m_connectionInterface = 0;
+    }
+}
+
+void Nepomuk::ResourceWatcher::addProperty(const Nepomuk::Types::Property& property)
+{
+    d->m_properties << property;
+}
+
+void Nepomuk::ResourceWatcher::addResource(const Nepomuk::Resource& res)
+{
+    d->m_resources << res;
+}
+
+void Nepomuk::ResourceWatcher::addType(const Nepomuk::Types::Class& type)
+{
+    d->m_types << type;
+}
+
+QList< Nepomuk::Types::Property > Nepomuk::ResourceWatcher::properties() const
+{
+    return d->m_properties;
+}
+
+QList<Nepomuk::Resource> Nepomuk::ResourceWatcher::resources() const
+{
+    return d->m_resources;
+}
+
+QList< Nepomuk::Types::Class > Nepomuk::ResourceWatcher::types() const
+{
+    return d->m_types;
+}
+
+void Nepomuk::ResourceWatcher::setProperties(const QList< Nepomuk::Types::Property >& properties_)
+{
+    d->m_properties = properties_;
+}
+
+void Nepomuk::ResourceWatcher::setResources(const QList< Nepomuk::Resource >& resources_)
+{
+    d->m_resources = resources_;
+}
+
+void Nepomuk::ResourceWatcher::setTypes(const QList< Nepomuk::Types::Class >& types_)
+{
+    d->m_types = types_;
+}
+
+void Nepomuk::ResourceWatcher::slotResourceCreated(const QString &res, const QStringList &types)
+{
+    emit resourceCreated(Nepomuk::Resource::fromResourceUri(KUrl(res)), convertUris(types));
+}
+
+void Nepomuk::ResourceWatcher::slotResourceRemoved(const QString &res, const QStringList &types)
+{
+    emit resourceRemoved(KUrl(res), convertUris(types));
+}
+
+void Nepomuk::ResourceWatcher::slotResourceTypeAdded(const QString &res, const QString &type)
+{
+    emit resourceTypeAdded(KUrl(res), KUrl(type));
+}
+
+void Nepomuk::ResourceWatcher::slotResourceTypeRemoved(const QString &res, const QString &type)
+{
+    emit resourceTypeRemoved(KUrl(res), KUrl(type));
+}
+
+void Nepomuk::ResourceWatcher::slotPropertyAdded(const QString& res, const QString& prop, const QDBusVariant& object)
+{
+    emit propertyAdded( Resource::fromResourceUri(KUrl(res)), Types::Property( KUrl(prop) ), object.variant() );
+}
+
+void Nepomuk::ResourceWatcher::slotPropertyRemoved(const QString& res, const QString& prop, const QDBusVariant& object)
+{
+    emit propertyRemoved( Resource::fromResourceUri(KUrl(res)), Types::Property( KUrl(prop) ), object.variant() );
+}
+
+#include "resourcewatcher.moc"
+
diff --git a/nepomuk/services/storage/lib/resourcewatcher.h b/nepomuk/services/storage/lib/resourcewatcher.h
new file mode 100644
index 0000000..ac4425d
--- /dev/null
+++ b/nepomuk/services/storage/lib/resourcewatcher.h
@@ -0,0 +1,266 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2011  Vishesh Handa <handa.vish@gmail.com>
+    Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+#ifndef RESOURCEWATCHER_H
+#define RESOURCEWATCHER_H
+
+#include <Nepomuk/Types/Class>
+#include <Nepomuk/Types/Property>
+#include <Nepomuk/Resource>
+
+#include <QtDBus/QDBusVariant>
+
+#include "nepomukdatamanagement_export.h"
+
+namespace Nepomuk {
+
+    /**
+     * \class ResourceWatcher resourcewatcher.h
+     *
+     * \brief Selectively monitor the nepomuk repository for changes.
+     *
+     * Resources may be monitored on the basis of types, properties, and uris.
+     *
+     * Changes may be monitored in one of the following ways:
+     * -# By resources -
+     *    Specify the exact resources that should be watched. Any changes made to the specified resources
+     *    (Excluding \ref nepomuk_dms_metadata) will be notified through the propertyAdded() and propertyRemoved()
+     *    signals. Notifications will also be sent if any of the watched resources is deleted.
+     * -# By resources and properties -
+     *    Specify the exact resources and their properties. Any changes made to the specified resources
+     *    which touch one of the specified properties will be notified through the propertyAdded() and propertyRemoved()
+     *    signals.
+     * -# By types -
+     *    Specific types may be specified via add/setType. If types are set, then notifications will be
+     *    sent for all new resources of that type. This includes property changes and resource creation and removal.
+     *    TODO: add flags that allow to only watch for resource creation and removal.
+     * -# By types and properties -
+     *    Both the types and properties may be specified. Notifications will be sent for property changes
+     *    in resource with the specified types.
+     *
+     * \section nepomuk_rw_examples Resource Watcher Usage Example
+     *
+     * The following code creates a new ResourceWatcher, configures it to listen to changes on the \c nmm:performer
+     * property on one specific resource \c res.
+     *
+     * \code
+     * Nepomuk::ResourceWatcher* watcher = new Nepomuk::ResourceWatcher(this);
+     * watcher->addResource(res);
+     * watcher->addProperty(NMM:performer());
+     * connect(watcher, SIGNAL(propertyAdded(Nepomuk::Resource, Nepomuk::Types::Property, QVariant)),
+     *         this, SLOT(slotPropertyChanged()));
+     * connect(watcher, SIGNAL(propertyRemoved(Nepomuk::Resource, Nepomuk::Types::Property, QVariant)),
+     *         this, SLOT(slotPropertyChanged()));
+     * rwatcher->start();
+     * \endcode
+     *
+     * \author Vishesh Handa <handa.vish@gmail.com>, Sebastian Trueg <trueg@kde.org>
+     *
+     * \ingroup nepomuk_datamanagement
+     */
+    class NEPOMUK_DATA_MANAGEMENT_EXPORT ResourceWatcher : public QObject
+    {
+        Q_OBJECT
+
+    public:
+        /**
+         * \brief Create a new %ResourceWatcher instance.
+         *
+         * This instance will not emit any signals before it has been configured
+         * and started.
+         */
+        ResourceWatcher( QObject* parent = 0 );
+
+        /**
+         * \brief Destructor.
+         */
+        virtual ~ResourceWatcher();
+
+        /**
+         * \brief Add a type to be watched.
+         *
+         * Every resource of this type will be watched for changes.
+         *
+         * \sa setTypes()
+         */
+        void addType( const Types::Class & type );
+
+        /**
+         * \brief Add a resource to be watched.
+         *
+         * Every change to this resource will be
+         * signalled, depending on the configured properties().
+         *
+         * \sa setResources()
+         */
+        void addResource( const Nepomuk::Resource & res );
+
+        /**
+         * \brief Add a property to be watched.
+         *
+         * Every change to a value of this property
+         * will be signalled, depending on the configured resources() or types().
+         *
+         * \sa setProperties()
+         */
+        void addProperty( const Types::Property & property );
+
+        /**
+         * \brief Set the types to be watched.
+         *
+         * Every resource having one of these types will be watched for changes.
+         *
+         * \sa addType()
+         */
+        void setTypes( const QList<Types::Class> & types_ );
+
+        /**
+         * \brief Set the resources to be watched.
+         *
+         * Every change to one of these resources will be
+         * signalled, depending on the configured properties().
+         *
+         * \sa addResource()
+         */
+        void setResources( const QList<Nepomuk::Resource> & resources_ );
+
+        /**
+         * \brief Set the properties to be watched.
+         *
+         * Every change to a value of any of these properties
+         * will be signalled, depending on the configured resources() or types().
+         *
+         * \sa addProperty()
+         */
+        void setProperties( const QList<Types::Property> & properties_ );
+
+        /**
+         * \brief The types that have been configured via addType() and setTypes().
+         *
+         * Every resource having one of these types will be watched
+         * for changes.
+         */
+        QList<Types::Class> types() const;
+
+        /**
+         * \brief The resources that have been configured via addResource() and setResources().
+         *
+         * Every change to one of these resources will be
+         * signalled, depending on the configured properties().
+         */
+        QList<Nepomuk::Resource> resources() const;
+
+        /**
+         * \brief The properties that have been configured via addProperty() and setProperties().
+         *
+         * Every change to a value of any of these properties
+         * will be signalled, depending on the configured resources() or types().
+         */
+        QList<Types::Property> properties() const;
+
+    public Q_SLOTS:
+        /**
+         * \brief Start the signalling of changes.
+         *
+         * Before calling this method no signal will be emitted. In
+         * combination with stop() this allows to suspend the watching.
+         * Calling start() multiple times has no effect.
+         */
+        bool start();
+
+        /**
+         * \brief Stop the signalling of changes.
+         *
+         * Allows to stop the watcher which has been started
+         * via start(). Calling stop() multiple times has no effect.
+         */
+        void stop();
+
+    Q_SIGNALS:
+        /**
+         * \brief This signal is emitted when a new resource is created.
+         * \param resource The newly created resource.
+         * \param types The types the new resource has. If types() have been configured this list will always
+         * contain one of the configured types.
+         */
+        void resourceCreated( const Nepomuk::Resource & resource, const QList<QUrl>& types ); //FIXME: Use either Resource or uri, not a mix
+
+        /**
+         * \brief This signal is emitted when a resource is deleted.
+         * \param uri The resource URI of the removed resource.
+         * \param types The types the removed resource had. If types() have been configured this list will always
+         * contain one of the configured types.
+         */
+        void resourceRemoved( const QUrl & uri, const QList<QUrl>& types );
+
+        /**
+         * \brief This signal is emitted when a type has been added to a resource. This does not include creation which
+         * is signalled via resourceCreated(). It only applies to changes in a resource's types.
+         * \param res The changed resource.
+         * \param type The newly added type. If types() have been configured it will be one of them.
+         */
+        void resourceTypeAdded( const Nepomuk::Resource & res, const Types::Class & type );
+
+        /**
+         * \brief This signal is emitted when a type has been removed from a resource.
+         *
+         * This does not include removal of entire resources which is signalled via resourceRemoved().
+         * It only applies to changes in a resource's types.
+         * \param res The changed resource.
+         * \param type The removed type. If types() have been configured it will be one of them.
+         */
+        void resourceTypeRemoved( const Nepomuk::Resource & res, const Types::Class & type );
+
+        /**
+         * \brief This signal is emitted when a property value is added.
+         * \param resource The changed resource.
+         * \param property The property which has a new value.
+         * \param value The newly added property value.
+         */
+        void propertyAdded( const Nepomuk::Resource & resource,
+                            const Nepomuk::Types::Property & property,
+                            const QVariant & value );
+
+        /**
+         * \brief This signal is emitted when a property value is removed.
+         * \param resource The changed resource.
+         * \param property The property which was changed.
+         * \param value The removed property value.
+         */
+        void propertyRemoved( const Nepomuk::Resource & resource,
+                              const Nepomuk::Types::Property & property,
+                              const QVariant & value );
+
+    private Q_SLOTS:
+        void slotResourceCreated(const QString& res, const QStringList& types);
+        void slotResourceRemoved(const QString& res, const QStringList& types);
+        void slotResourceTypeAdded(const QString& res, const QString& type);
+        void slotResourceTypeRemoved(const QString& res, const QString& type);
+        void slotPropertyAdded(const QString& res, const QString& prop, const QDBusVariant& object);
+        void slotPropertyRemoved(const QString& res, const QString& prop, const QDBusVariant& object);
+
+    private:
+        class Private;
+        Private * d;
+    };
+}
+
+#endif // RESOURCEWATCHER_H
diff --git a/nepomuk/services/storage/lib/simpleresource.cpp b/nepomuk/services/storage/lib/simpleresource.cpp
new file mode 100644
index 0000000..3d529bb
--- /dev/null
+++ b/nepomuk/services/storage/lib/simpleresource.cpp
@@ -0,0 +1,278 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "simpleresource.h"
+
+#include <QtCore/QHashIterator>
+#include <QtCore/QSharedData>
+#include <QtCore/QVariant>
+#include <QtCore/QDebug>
+
+#include <Soprano/Node>
+#include <Soprano/LiteralValue>
+#include <Soprano/Vocabulary/RDF>
+
+
+namespace {
+QAtomicInt s_idCnt;
+
+QUrl createBlankUri()
+{
+    // convert int to string (a...z,aa...az,ba....bz,...)
+    int idCnt = s_idCnt.fetchAndAddRelaxed(1);
+    QByteArray id;
+    do {
+        const int rest = idCnt%26;
+        id.append('a' + rest);
+        idCnt -= rest;
+        idCnt /= 26;
+    } while(idCnt > 0);
+
+    const QUrl uri = QString(QLatin1String("_:") + id);
+    return uri;
+}
+}
+
+class Nepomuk::SimpleResource::Private : public QSharedData
+{
+public:
+    QUrl m_uri;
+    PropertyHash m_properties;
+};
+
+Nepomuk::SimpleResource::SimpleResource(const QUrl& uri)
+{
+    d = new Private();
+    setUri(uri);
+}
+
+Nepomuk::SimpleResource::SimpleResource(const PropertyHash& properties)
+{
+    d = new Private();
+    setUri(QUrl());
+    setProperties(properties);
+}
+
+Nepomuk::SimpleResource::SimpleResource(const SimpleResource& other)
+    : d(other.d)
+{
+}
+
+Nepomuk::SimpleResource::~SimpleResource()
+{
+}
+
+Nepomuk::SimpleResource & Nepomuk::SimpleResource::operator =(const Nepomuk::SimpleResource &other)
+{
+    d = other.d;
+    return *this;
+}
+
+QUrl Nepomuk::SimpleResource::uri() const
+{
+    return d->m_uri;
+}
+
+void Nepomuk::SimpleResource::setUri(const QUrl& uri)
+{
+    if(uri.isEmpty())
+        d->m_uri = createBlankUri();
+    else
+        d->m_uri = uri;
+}
+
+namespace {
+    Soprano::Node convertIfBlankNode( const Soprano::Node & n ) {
+        if( n.isResource() && n.uri().toString().startsWith("_:") ) {
+            return Soprano::Node( n.uri().toString().mid(2) ); // "_:" take 2 characters
+        }
+        return n;
+    }
+}
+
+QList< Soprano::Statement > Nepomuk::SimpleResource::toStatementList() const
+{
+    QList<Soprano::Statement> list;
+    QHashIterator<QUrl, QVariant> it( d->m_properties );
+    while( it.hasNext() ) {
+        it.next();
+
+        Soprano::Node object;
+        if( it.value().type() == QVariant::Url )
+            object = it.value().toUrl();
+        else
+            object = Soprano::LiteralValue( it.value() );
+
+        list << Soprano::Statement( convertIfBlankNode( d->m_uri ),
+                                    it.key(),
+                                    convertIfBlankNode( object ) );
+    }
+    return list;
+}
+
+bool Nepomuk::SimpleResource::isValid() const
+{
+    // We do not check if m_uri.isValid() as a blank uri of the form "_:daf" would be invalid
+    if(d->m_uri.isEmpty() || d->m_properties.isEmpty()) {
+        return false;
+    }
+
+    // properties cannot have empty values
+    PropertyHash::const_iterator end = d->m_properties.constEnd();
+    for(PropertyHash::const_iterator it = d->m_properties.constBegin(); it != end; ++it) {
+        if(!it.value().isValid()) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool Nepomuk::SimpleResource::operator ==(const Nepomuk::SimpleResource &other) const
+{
+    return d->m_uri == other.d->m_uri && d->m_properties == other.d->m_properties;
+}
+
+Nepomuk::PropertyHash Nepomuk::SimpleResource::properties() const
+{
+    return d->m_properties;
+}
+
+bool Nepomuk::SimpleResource::contains(const QUrl &property) const
+{
+    return d->m_properties.contains(property);
+}
+
+bool Nepomuk::SimpleResource::contains(const QUrl &property, const QVariant &value) const
+{
+    return d->m_properties.contains(property, value);
+}
+
+bool Nepomuk::SimpleResource::containsNode(const QUrl &property, const Soprano::Node &node) const
+{
+    if(node.isLiteral())
+        return contains(property, node.literal().variant());
+    else if(node.isResource())
+        return contains(property, node.uri());
+    else
+        return false;
+}
+
+void Nepomuk::SimpleResource::setPropertyNode(const QUrl &property, const Soprano::Node &value)
+{
+    d->m_properties.remove(property);
+    addPropertyNode(property, value);
+}
+
+void Nepomuk::SimpleResource::setProperty(const QUrl &property, const QVariant &value)
+{
+    d->m_properties.remove(property);
+    addProperty(property, value);
+}
+
+void Nepomuk::SimpleResource::setProperty(const QUrl& property, const Nepomuk::SimpleResource& res)
+{
+    setProperty(property, res.uri());
+}
+
+
+void Nepomuk::SimpleResource::setProperty(const QUrl &property, const QVariantList &values)
+{
+    d->m_properties.remove(property);
+    foreach(const QVariant& v, values) {
+        addProperty(property, v);
+    }
+}
+
+void Nepomuk::SimpleResource::addProperty(const QUrl &property, const QVariant &value)
+{
+    // QMultiHash even stores the same key/value pair multiple times!
+    if(!d->m_properties.contains(property, value))
+        d->m_properties.insertMulti(property, value);
+}
+
+void Nepomuk::SimpleResource::addProperty(const QUrl& property, const Nepomuk::SimpleResource& res)
+{
+    addProperty(property, res.uri());
+}
+
+void Nepomuk::SimpleResource::addPropertyNode(const QUrl &property, const Soprano::Node &node)
+{
+    if(node.isResource())
+        addProperty(property, QVariant(node.uri()));
+    else if(node.isLiteral())
+        addProperty(property, node.literal().variant());
+    // else do nothing
+}
+
+void Nepomuk::SimpleResource::removeProperty(const QUrl &property, const QVariant &value)
+{
+    d->m_properties.remove(property, value);
+}
+
+void Nepomuk::SimpleResource::removeProperty(const QUrl &property)
+{
+    d->m_properties.remove(property);
+}
+
+void Nepomuk::SimpleResource::addType(const QUrl &type)
+{
+    addProperty(Soprano::Vocabulary::RDF::type(), type);
+}
+
+void Nepomuk::SimpleResource::setTypes(const QList<QUrl> &types)
+{
+    QVariantList values;
+    foreach(const QUrl& type, types) {
+        values << type;
+    }
+    setProperty(Soprano::Vocabulary::RDF::type(), values);
+}
+
+void Nepomuk::SimpleResource::setProperties(const Nepomuk::PropertyHash &properties)
+{
+    d->m_properties = properties;
+}
+
+void Nepomuk::SimpleResource::clear()
+{
+    d->m_properties.clear();
+}
+
+void Nepomuk::SimpleResource::addProperties(const Nepomuk::PropertyHash &properties)
+{
+    d->m_properties += properties;
+}
+
+QVariantList Nepomuk::SimpleResource::property(const QUrl &property) const
+{
+    return d->m_properties.values(property);
+}
+
+uint Nepomuk::qHash(const SimpleResource& res)
+{
+    return qHash(res.uri());
+}
+
+QDebug Nepomuk::operator<<(QDebug dbg, const Nepomuk::SimpleResource& res)
+{
+    return dbg << res.uri() << res.properties();
+}
diff --git a/nepomuk/services/storage/lib/simpleresource.h b/nepomuk/services/storage/lib/simpleresource.h
new file mode 100644
index 0000000..c6c3b04
--- /dev/null
+++ b/nepomuk/services/storage/lib/simpleresource.h
@@ -0,0 +1,181 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2010-2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SIMPLERESOURCE_H
+#define SIMPLERESOURCE_H
+
+#include <QtCore/QUrl>
+#include <QtCore/QMultiHash>
+#include <QtCore/QList>
+#include <QtCore/QSharedDataPointer>
+
+#include <Soprano/Statement>
+
+#include "nepomukdatamanagement_export.h"
+
+namespace Nepomuk {
+
+typedef QMultiHash<QUrl, QVariant> PropertyHash;
+
+/**
+ * \class SimpleResource simpleresource.h Nepomuk/SimpleResource
+ *
+ * \brief Represents a snapshot of one %Nepomuk resource.
+ *
+ * \author Vishesh Handa <handa.vish@gmail.com>, Sebastian Trueg <trueg@kde.org>
+ */
+class NEPOMUK_DATA_MANAGEMENT_EXPORT SimpleResource
+{
+public:
+    explicit SimpleResource(const QUrl& uri = QUrl());
+    SimpleResource(const PropertyHash& properties);
+    SimpleResource(const SimpleResource& other);
+    virtual ~SimpleResource();
+
+    SimpleResource& operator=(const SimpleResource& other);
+
+    bool operator==(const SimpleResource& other) const;
+
+    QUrl uri() const;
+
+    /**
+     * Setting an invalid/empty uri will create a new random ID.
+     * This allows to reuse SimpleResource instances.
+     */
+    void setUri( const QUrl & uri );
+
+    bool contains(const QUrl& property) const;
+    bool contains(const QUrl& property, const QVariant& value) const;
+    bool containsNode(const QUrl& property, const Soprano::Node& value) const;
+
+    /**
+     * Clear the resource, remove all properties.
+     */
+    void clear();
+
+    /**
+     * Set the properties, replacing the existing properties.
+     */
+    void setProperties(const PropertyHash& properties);
+
+    /**
+     * Add a set of properties to the existing ones.
+     */
+    void addProperties(const PropertyHash& properties);
+
+    /**
+     * Set a property overwriting existing values.
+     * \param property The property to set
+     * \param value The value of the property.
+     */
+    void setProperty(const QUrl& property, const QVariant& value);
+
+    /**
+     * Set a property overwriting existing values.
+     * \param property The property to set
+     * \param values The values of the property.
+     */
+    void setProperty(const QUrl& property, const QVariantList& values);
+
+    /**
+     * Set a property overwriting existing values.
+     * \param property The property to set
+     * \param set The values of the property.
+     */
+    void setProperty(const QUrl& property, const SimpleResource& res);
+
+    /**
+     * Set a property overwriting existing values.
+     * \param property The property to set
+     * \param value The value of the property. Will be converted to a QVariant.
+     */
+    void setPropertyNode(const QUrl& property, const Soprano::Node& value);
+
+    /**
+     * Add a property. This allows to add more than one value for a property.
+     * \param property The property to set
+     * \param value The value of the property.
+     */
+    void addProperty(const QUrl& property, const QVariant& value);
+
+    /**
+     * Add a property. This allows to add more than one value for a property.
+     * \param property The property to set
+     * \param res The value of the property.
+     */
+    void addProperty(const QUrl& property, const SimpleResource& res);
+
+    /**
+     * Add a property.
+     * \param property The property to set
+     * \param value The value of the property. Will be converted to a QVariant.
+     */
+    void addPropertyNode(const QUrl& property, const Soprano::Node& value);
+
+    void removeProperty(const QUrl& property, const QVariant& value);
+    void removeProperty(const QUrl& property);
+
+    /**
+     * A convenience method which adds a property of type rdf:type.
+     * \param type The type to add to the resource. Must be the URI of an RDF class.
+     */
+    void addType(const QUrl& type);
+
+    /**
+     * A convenience method which sets the property of type rdf:type.
+     * \param types The types to set to the resource. Must be URIs of RDF classes.
+     */
+    void setTypes(const QList<QUrl>& types);
+
+    /**
+     * Get all values for \p property.
+     */
+    QVariantList property(const QUrl& property) const;
+
+    /**
+     * \return All properties.
+     */
+    PropertyHash properties() const;
+
+    /**
+     * Converts the resource into a list of statements. None of the statements will
+     * have a valid context set.
+     */
+    QList<Soprano::Statement> toStatementList() const;
+
+    /**
+     * \return \p true if the resource is valid, ie. if it has a valid uri() and
+     * a non-empty list of properties.
+     */
+    bool isValid() const;
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+NEPOMUK_DATA_MANAGEMENT_EXPORT QDebug operator<<(QDebug dbg, const Nepomuk::SimpleResource& res);
+
+NEPOMUK_DATA_MANAGEMENT_EXPORT uint qHash(const SimpleResource& res);
+}
+
+#endif
diff --git a/nepomuk/services/storage/lib/simpleresourcegraph.cpp b/nepomuk/services/storage/lib/simpleresourcegraph.cpp
new file mode 100644
index 0000000..419a949
--- /dev/null
+++ b/nepomuk/services/storage/lib/simpleresourcegraph.cpp
@@ -0,0 +1,220 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "simpleresourcegraph.h"
+#include "simpleresource.h"
+#include "datamanagement.h"
+
+#include <QtCore/QSharedData>
+#include <QtCore/QHash>
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+
+#include <KRandom>
+
+class Nepomuk::SimpleResourceGraph::Private : public QSharedData
+{
+public:
+    QHash<QUrl, SimpleResource> resources;
+};
+
+
+Nepomuk::SimpleResourceGraph::SimpleResourceGraph()
+    : d(new Private)
+{
+}
+
+Nepomuk::SimpleResourceGraph::SimpleResourceGraph(const SimpleResource& resource)
+    : d(new Private)
+{
+    insert(resource);
+}
+
+Nepomuk::SimpleResourceGraph::SimpleResourceGraph(const QList<SimpleResource>& resources)
+    : d(new Private)
+{
+    Q_FOREACH(const SimpleResource& res, resources) {
+        insert(res);
+    }
+}
+
+Nepomuk::SimpleResourceGraph::SimpleResourceGraph(const QSet<SimpleResource>& resources)
+    : d(new Private)
+{
+    Q_FOREACH(const SimpleResource& res, resources) {
+        insert(res);
+    }
+}
+
+Nepomuk::SimpleResourceGraph::SimpleResourceGraph(const SimpleResourceGraph& other)
+    : d(other.d)
+{
+}
+
+Nepomuk::SimpleResourceGraph::~SimpleResourceGraph()
+{
+}
+
+Nepomuk::SimpleResourceGraph & Nepomuk::SimpleResourceGraph::operator=(const Nepomuk::SimpleResourceGraph &other)
+{
+    d = other.d;
+    return *this;
+}
+
+void Nepomuk::SimpleResourceGraph::insert(const SimpleResource &res)
+{
+    d->resources.insert(res.uri(), res);
+}
+
+Nepomuk::SimpleResourceGraph& Nepomuk::SimpleResourceGraph::operator<<(const SimpleResource &res)
+{
+    insert(res);
+    return *this;
+}
+
+void Nepomuk::SimpleResourceGraph::remove(const QUrl &uri)
+{
+    d->resources.remove(uri);
+}
+
+void Nepomuk::SimpleResourceGraph::remove(const SimpleResource &res)
+{
+    if( contains( res ) )
+        remove( res.uri() );
+}
+
+void Nepomuk::SimpleResourceGraph::add(const QUrl &uri, const QUrl &property, const QVariant &value)
+{
+    if(!uri.isEmpty()) {
+        d->resources[uri].setUri(uri);
+        d->resources[uri].addProperty(property, value);
+    }
+}
+
+void Nepomuk::SimpleResourceGraph::set(const QUrl &uri, const QUrl &property, const QVariant &value)
+{
+    removeAll(uri, property);
+    add(uri, property, value);
+}
+
+void Nepomuk::SimpleResourceGraph::remove(const QUrl &uri, const QUrl &property, const QVariant &value)
+{
+    QHash< QUrl, SimpleResource >::iterator it = d->resources.find( uri );
+    if( it != d->resources.end() ) {
+        it.value().removeProperty(property, value);
+    }
+}
+
+void Nepomuk::SimpleResourceGraph::removeAll(const QUrl &uri, const QUrl &property)
+{
+    QHash< QUrl, SimpleResource >::iterator it = d->resources.find( uri );
+    if( it != d->resources.end() ) {
+        it.value().removeProperty(property);
+    }
+}
+
+bool Nepomuk::SimpleResourceGraph::contains(const QUrl &uri) const
+{
+    return d->resources.contains(uri);
+}
+
+bool Nepomuk::SimpleResourceGraph::containsAny(const QUrl &res, const QUrl &property) const
+{
+    QHash< QUrl, SimpleResource >::const_iterator it = d->resources.constFind( res );
+    if( it == d->resources.constEnd() )
+        return false;
+
+    return it.value().contains(property);
+}
+
+bool Nepomuk::SimpleResourceGraph::contains(const SimpleResource &res) const
+{
+    QHash< QUrl, SimpleResource >::const_iterator it = d->resources.find( res.uri() );
+    if( it == d->resources.constEnd() )
+        return false;
+
+    return res == it.value();
+}
+
+Nepomuk::SimpleResource Nepomuk::SimpleResourceGraph::operator[](const QUrl &uri) const
+{
+    return d->resources[uri];
+}
+
+QSet<Nepomuk::SimpleResource> Nepomuk::SimpleResourceGraph::toSet() const
+{
+    return QSet<SimpleResource>::fromList(toList());
+}
+
+QList<Nepomuk::SimpleResource> Nepomuk::SimpleResourceGraph::toList() const
+{
+    return d->resources.values();
+}
+
+void Nepomuk::SimpleResourceGraph::clear()
+{
+    d->resources.clear();
+}
+
+bool Nepomuk::SimpleResourceGraph::isEmpty() const
+{
+    return d->resources.isEmpty();
+}
+
+int Nepomuk::SimpleResourceGraph::count() const
+{
+    return d->resources.count();
+}
+
+namespace {
+QVariant nodeToVariant(const Soprano::Node& node) {
+    if(node.isResource())
+        return node.uri();
+    else if(node.isBlank())
+        return QUrl(QLatin1String("_:") + node.identifier());
+    else
+        return node.literal().variant();
+}
+}
+
+void Nepomuk::SimpleResourceGraph::addStatement(const Soprano::Statement &s)
+{
+    const QUrl uri = nodeToVariant(s.subject()).toUrl();
+    const QVariant value = nodeToVariant(s.object());
+    d->resources[uri].setUri(uri);
+    d->resources[uri].addProperty(s.predicate().uri(), value);
+}
+
+void Nepomuk::SimpleResourceGraph::addStatement(const Soprano::Node& subject, const Soprano::Node& predicate, const Soprano::Node& object)
+{
+    addStatement( Soprano::Statement( subject, predicate, object ) );
+}
+
+
+KJob* Nepomuk::SimpleResourceGraph::save(const KComponentData& component) const
+{
+    return Nepomuk::storeResources(*this, Nepomuk::IdentifyNew, Nepomuk::NoStoreResourcesFlags, QHash<QUrl, QVariant>(), component);
+}
+
+QDebug Nepomuk::operator<<(QDebug dbg, const Nepomuk::SimpleResourceGraph& graph)
+{
+    return dbg << graph.toList();
+}
diff --git a/nepomuk/services/storage/lib/simpleresourcegraph.h b/nepomuk/services/storage/lib/simpleresourcegraph.h
new file mode 100644
index 0000000..6a3a0ee
--- /dev/null
+++ b/nepomuk/services/storage/lib/simpleresourcegraph.h
@@ -0,0 +1,109 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SIMPLERESOURCEGRAPH_H
+#define SIMPLERESOURCEGRAPH_H
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QList>
+#include <QtCore/QSet>
+#include <QtCore/QUrl>
+#include <QtCore/QMetaType>
+
+#include <KGlobal>
+
+#include "nepomukdatamanagement_export.h"
+
+class KJob;
+namespace Soprano {
+class Statement;
+class Node;
+}
+namespace Nepomuk {
+class SimpleResource;
+
+class NEPOMUK_DATA_MANAGEMENT_EXPORT SimpleResourceGraph
+{
+public:
+    SimpleResourceGraph();
+    SimpleResourceGraph(const SimpleResource& resource);
+    SimpleResourceGraph(const QList<SimpleResource>& resources);
+    SimpleResourceGraph(const QSet<SimpleResource>& resources);
+    SimpleResourceGraph(const SimpleResourceGraph& other);
+    ~SimpleResourceGraph();
+
+    SimpleResourceGraph& operator=(const SimpleResourceGraph& other);
+
+    /**
+     * Adds a resource to the graph. An invalid resource will get a
+     * new blank node as resource URI.
+     */
+    void insert(const SimpleResource& res);
+    SimpleResourceGraph& operator<<(const SimpleResource& res);
+
+    void remove(const QUrl& uri);
+    void remove(const SimpleResource& res);
+
+    void add(const QUrl& uri, const QUrl& property, const QVariant& value);
+    void set(const QUrl& uri, const QUrl& property, const QVariant& value);
+
+    void remove(const QUrl& uri, const QUrl& property, const QVariant& value);
+    void removeAll(const QUrl& uri, const QUrl& property);
+
+    void clear();
+
+    int count() const;
+    bool isEmpty() const;
+
+    bool contains(const SimpleResource& res) const;
+    bool contains(const QUrl& res) const;
+    bool containsAny(const QUrl& res, const QUrl& property) const;
+
+    SimpleResource operator[](const QUrl& uri) const;
+
+    QSet<SimpleResource> toSet() const;
+    QList<SimpleResource> toList() const;
+
+    void addStatement(const Soprano::Statement& statement);
+    void addStatement( const Soprano::Node & subject, const Soprano::Node & predicate,
+                       const Soprano::Node & object );
+    /**
+     * Save the graph to the Nepomuk database.
+     * \return A job that will perform the saving
+     * and emit the result() signal once done.
+     * Use the typical KJob error handling methods.
+     *
+     * \param component The component which should be given to
+     * Nepomuk for it to relate the newly created data to it.
+     *
+     * \sa Nepomuk::storeResources()
+     */
+    KJob* save(const KComponentData& component = KGlobal::mainComponent()) const;
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+NEPOMUK_DATA_MANAGEMENT_EXPORT QDebug operator<<(QDebug dbg, const Nepomuk::SimpleResourceGraph& graph);
+}
+
+#endif
diff --git a/nepomuk/services/storage/nepomukcore.cpp b/nepomuk/services/storage/nepomukcore.cpp
index f9ed06c..452c98b 100644
--- a/nepomuk/services/storage/nepomukcore.cpp
+++ b/nepomuk/services/storage/nepomukcore.cpp
@@ -83,10 +83,6 @@ void Nepomuk::Core::slotRepositoryOpened( Repository* repo, bool success )
 
 void Nepomuk::Core::slotOntologiesLoaded()
 {
-    // once ontologies are updated we should update the query prefixes
-    m_repository->setEnableQueryPrefixExpansion(false);
-    m_repository->setEnableQueryPrefixExpansion(true);
-
     // the first time this is a very long procedure. Thus, we do it while Nepomuk is active although then some queries might return invalid results
     m_repository->updateInference();
 
diff --git a/nepomuk/services/storage/nepomukstorage.desktop b/nepomuk/services/storage/nepomukstorage.desktop
index 4fdaa3f..30d5ddc 100644
--- a/nepomuk/services/storage/nepomukstorage.desktop
+++ b/nepomuk/services/storage/nepomukstorage.desktop
@@ -10,6 +10,7 @@ Name[ast]=Atroxamientu de datos Nepomuk
 Name[be@latin]=Schovišča dla źviestak „Nepomuk”
 Name[bg]=Хранилище Nepomuk
 Name[bn_IN]=Nepomuk ডাটা সংরক্ষণব্যবস্থা
+Name[bs]=Nepomukovo skladište
 Name[ca]=Emmagatzematge de dades del Nepomuk
 Name[ca@valencia]=Emmagatzematge de dades del Nepomuk
 Name[cs]=Datové úložiště Nepomuku
@@ -74,6 +75,7 @@ Name[te]=Nepomuk డాటా నిల్వ
 Name[tg]=Захирагоҳи Nepomuk
 Name[th]=ที่จัดเก็บข้อมูลของ Nepomuk
 Name[tr]=Nepomuk Veri Depolama
+Name[ug]=Nepomuk سانلىق مەلۇمات ساقلاش
 Name[uk]=Збереження даних Nepomuk
 Name[wa]=Sitocaedje di dnêyes Nepomuk
 Name[x-test]=xxNepomuk Data Storagexx
@@ -84,6 +86,7 @@ Comment[ar]=خدمة تخزين بيانات نبومك الأساسية
 Comment[ast]=Serviciu principal d'atroxamientu de datos Nepomuk
 Comment[be@latin]=Hałoŭnaja słužba „Nepomuk” dla zachoŭvańnia źviestak
 Comment[bg]=Основното място, където се съхраняват данните на Nepomuk
+Comment[bs]=Jezgarni servis Nepomuka za skladištenje podataka
 Comment[ca]=El servei d'emmagatzematge de dades del nucli del Nepomuk
 Comment[ca@valencia]=El servei d'emmagatzematge de dades del nucli del Nepomuk
 Comment[cs]=Jádro služby Datové úložiště Nepomuku
@@ -147,6 +150,7 @@ Comment[te]=ఆధార Nepomuk డాటా నిల్వ సేవ
 Comment[tg]=Хидмати асосии захирагоҳи Nepomuk
 Comment[th]=บริการจัดเก็บข้อมูลระดับแกนของ Nepomik
 Comment[tr]=Nepomuk Ana veri depolama servisi
+Comment[ug]=Nepomuk يادرولۇق سانلىق مەلۇمات ساقلاش مۇلازىمىتى
 Comment[uk]=Ядро служби збереження даних Nepomuk
 Comment[wa]=Li siervice cour di stocaedje di dnêyes Nepomuk
 Comment[x-test]=xxThe Core Nepomuk data storage servicexx
diff --git a/nepomuk/services/storage/nepomukstorage.notifyrc b/nepomuk/services/storage/nepomukstorage.notifyrc
index 485c082..47e3012 100644
--- a/nepomuk/services/storage/nepomukstorage.notifyrc
+++ b/nepomuk/services/storage/nepomukstorage.notifyrc
@@ -4,6 +4,7 @@ Name=Semantic Data Storage
 Name[ar]=مخزن البيانات الدلالية
 Name[ast]=Atroxamientu de datos semánticos
 Name[bg]=Хранилище за семантични данни
+Name[bs]=Semantičko skladištenje podataka
 Name[ca]=Emmagatzematge de dades semàntiques
 Name[ca@valencia]=Emmagatzematge de dades semàntiques
 Name[cs]=Úložistě sémantických dat
@@ -33,7 +34,6 @@ Name[kn]=ಸೆಮಾಂಟಿಕ್ ದತ್ತ ಸಂಗ್ರಹ
 Name[ko]=시맨틱 데이터 저장소
 Name[lt]=Semantinių duomenų saugykla
 Name[lv]=Semantisko datu glabātuve
-Name[mai]=सेमांटिक डाटा भंडार
 Name[ml]=സെമാന്റിക്ക് ഡേറ്റാ സംഭരണം
 Name[nb]=Semantisk datalagring
 Name[nds]=Semantsch Datenaflaag
@@ -56,6 +56,7 @@ Name[sv]=Semantisk datalagring
 Name[tg]=Захирагоҳи маъноии маълумот
 Name[th]=ที่จัดเก็บข้อมูลของระบบค้นหา
 Name[tr]=Anlamsal Veri Depolama
+Name[ug]=Semantic سانلىق مەلۇمات ساقلاش
 Name[uk]=Сховище семантичних даних
 Name[x-test]=xxSemantic Data Storagexx
 Name[zh_CN]=语义学数据存储
@@ -64,6 +65,7 @@ Comment=Semantic Desktop
 Comment[ar]=سطح المكتب الدلالي
 Comment[ast]=Escritoriu semánticu
 Comment[bg]=Семантичен работен плот
+Comment[bs]=Semantička površ
 Comment[ca]=Escriptori semàntic
 Comment[ca@valencia]=Escriptori semàntic
 Comment[cs]=Sémantický desktop
@@ -93,7 +95,6 @@ Comment[kn]=ಸೆಮಾಂಟಿಕ್ ಗಣಕತೆರೆ
 Comment[ko]=시맨틱 데스크톱
 Comment[lt]=Semantinis darbastalis
 Comment[lv]=Semantiskā darbvirsma
-Comment[mai]=सेमांटिक डेस्कटॉप
 Comment[ml]=സെമാന്റിക്ക് പണിയിടം
 Comment[nb]=Semantisk skrivebord
 Comment[nds]=Schriefdischbeslöteln
@@ -104,6 +105,7 @@ Comment[pl]=Pulpit semantyczny
 Comment[pt]=Ambiente de Trabalho Semântico
 Comment[pt_BR]=Área de trabalho semântica
 Comment[ro]=Birou semantic
+Comment[ru]=Служба семантических связей Nepomuk
 Comment[sk]=Sémantický desktop
 Comment[sl]=Semantično namizje
 Comment[sr]=Семантичка површ
@@ -114,6 +116,7 @@ Comment[sv]=Semantiskt skrivbord
 Comment[tg]=Мизи кории маъноӣ
 Comment[th]=ระบบค้นหาผ่านพื้นที่ทำงาน
 Comment[tr]=Anlamsal Masaüstü
+Comment[ug]=Semantic ئۈستەلئۈستى
 Comment[uk]=Семантична стільниця
 Comment[x-test]=xxSemantic Desktopxx
 Comment[zh_CN]=语义学桌面
@@ -124,6 +127,7 @@ Name=Failed to start Nepomuk
 Name[ar]=فشل تشغيل نِيبوموك
 Name[ast]=Fallu al entamar Nepomuk
 Name[bg]=Грешка при зареждане на Nepomuk
+Name[bs]=Neuspelo pokretanje Nepomuka
 Name[ca]=Ha fallat en iniciar el Nepomuk
 Name[ca@valencia]=Ha fallat en iniciar el Nepomuk
 Name[cs]=Nepovedlo se spustit Nepomuk
@@ -156,7 +160,6 @@ Name[kn]=ನೆಪೋಮುಕ್ ಅನ್ನು ಆರಂಭಿಸುವಲ್
 Name[ko]=Nepomuk을 시작할 수 없음
 Name[lt]=Nepavyko paleisti Nepomuk
 Name[lv]=Neizdevās palaist Nepomuk
-Name[mai]=Nepomuk चालू नही कर पाए
 Name[mk]=Не успеа стартувањето на Непомук
 Name[ml]=നെപ്പോമുക്ക് ആരംഭിയ്ക്കുന്നതില്‍ പരാജയപ്പെട്ടു
 Name[nb]=Klarte ikke å starte Nepomuk
@@ -180,6 +183,7 @@ Name[sv]=Misslyckades starta Nepomuk
 Name[tg]=Оғози Nepomuk қатъ шуд
 Name[th]=ล้มเหลวในการเริ่มบริการ Nepomuk
 Name[tr]=Nepomuk başlatılamadı
+Name[ug]=Nepomuk نى قوزغىتالمىدى
 Name[uk]=Не вдалося запустити Nepomuk
 Name[x-test]=xxFailed to start Nepomukxx
 Name[zh_CN]=启动 Nepomuk 失败
@@ -187,6 +191,8 @@ Name[zh_TW]=無法啟動 Nepomuk
 Comment=The Nepomuk Semantic Desktop system could not be started
 Comment[ar]=لم يمكن بِدؤ نظام نِبوموك لسطح المكتب الدلالي
 Comment[ast]=Nun pudo entamase'l sistema d'Escritoriu Semánticu Nepomuk
+Comment[bg]=Системата за семантичен работен плот Nepomuk не може да се зареди
+Comment[bs]=Sistem semantičke površi Nepomuk ne može da se pokrene
 Comment[ca]=El sistema de l'escriptori semàntic del Nepomuk no s'ha pogut engegar
 Comment[ca@valencia]=El sistema de l'escriptori semàntic del Nepomuk no s'ha pogut engegar
 Comment[cs]=Systém pro sémantický desktop Nepomuk nemohl být spuštěn
@@ -238,6 +244,7 @@ Comment[sr@latin]=Sistem semantičke površi Nepomuk ne može da se pokrene
 Comment[sv]=Det semantiska skrivbordssystemet Nepomuk kunde inte startas
 Comment[th]=ไม่สามารถเริ่มการทำงานของ Nepomuk - ระบบค้นหาของพื้นที่ทำงาน ได้
 Comment[tr]=Nepomuk Semantik Masaüstü sistemi başlatılamadı
+Comment[ug]=Nepomuk Semantic ئۈستەلئۈستى سىستېمىسىنى قوزغىتالمىدى
 Comment[uk]=Не вдалося запустити систему семантичної стільниці Nepomuk
 Comment[x-test]=xxThe Nepomuk Semantic Desktop system could not be startedxx
 Comment[zh_CN]=无法启动 Nepomuk 语义桌面系统
@@ -250,6 +257,7 @@ Name[ar]=تحويل بيانات نبومك
 Name[ast]=Convirtiendo datos de Nepomuk
 Name[be@latin]=Pieraŭtvareńnie źviestak „Nepomuk”
 Name[bg]=Преобразуване данните на Nepomuk
+Name[bs]=Pretvaram Nepomukove podatke
 Name[ca]=S'estan convertint les dades del Nepomuk
 Name[ca@valencia]=S'estan convertint les dades del Nepomuk
 Name[cs]=Probíhá převod dat Nepomuku
@@ -313,6 +321,7 @@ Name[te]=నెపోమక్ దత్తాంశమును మార్ప
 Name[tg]=Служба Nepomuk Data Storage
 Name[th]=ทำการแปลงข้อมูลของ Nepomuk
 Name[tr]=Nepomuk verileri dönüştürülüyor
+Name[ug]=Nepomuk سانلىق-مەلۇماتىنى ئايلاندۇرۇۋاتىدۇ
 Name[uk]=Перетворення даних Nepomuk
 Name[wa]=Dji coviersêye les dinêyes Nepomuk
 Name[x-test]=xxConverting Nepomuk dataxx
@@ -322,6 +331,8 @@ Comment=All Nepomuk data is converted to a new storage backend
 Comment[ar]=تم تحويل كامل بيانات نبومك إلى منتهى خلفي جديد للتخزين
 Comment[ast]=Tolos datos de Nepomuk tan convertíos al nuevu motor d'atroxamientu
 Comment[be@latin]=Usie źviestki „Nepomuk” pieraŭtvoranyja dla novaha schovišča.
+Comment[bg]=Всички данни от Nepomuk са преобразувани към новото ядро за съхранение
+Comment[bs]=Svi Nepomukovi podaci su pretvoreni za novu skladišnu pozadinu
 Comment[ca]=S'estan convertint totes les dades del Nepomuk a un nou dorsal d'emmagatzematge
 Comment[ca@valencia]=S'estan convertint totes les dades del Nepomuk a un nou dorsal d'emmagatzematge
 Comment[cs]=Všechna data Nepomuku převedena do nového úložného systému
@@ -384,6 +395,7 @@ Comment[ta]=All Nepomuk data is converted to a new storage backend
 Comment[te]=నెపోమక్ యొక్క మొత్తం డాటా కొత్త నిల్వ బ్యాకెండ్‌కు మార్పడి చేయబడింది
 Comment[th]=ข้อมูลทั้งหมดของ Nepomuk ได้ถูกแปลงไปยังแบ็คเอนด์ตัวใหม่แล้ว
 Comment[tr]=Tüm Nepomuk verileri yeni depolama arka ucuna dönüştürüldü
+Comment[ug]=ھەممە Nepomuk سانلىق مەلۇمات يېڭى ساقلاش ئارقا ئۇچىغا ئايلاندۇرۇلدى
 Comment[uk]=Всі дані Nepomuk перетворено для нового сервера зберігання
 Comment[wa]=Totes les dnêyes di Nepomuk sont coviersêyes dins on novea programe fondmint di stocaedje
 Comment[x-test]=xxAll Nepomuk data is converted to a new storage backendxx
@@ -397,6 +409,7 @@ Name[ar]=فشلت علمية تحويل بيانات نبومك
 Name[ast]=Conversión de datos Nepomuk fallida
 Name[be@latin]=Nie ŭdałosia pieraŭtvaryć źviestki „Nepomuk”.
 Name[bg]=Грешка при преобразуване данните на Nepomuk
+Name[bs]=Propalo pretvaranje Nepomukovih podataka
 Name[ca]=La conversió de dades del Nepomuk ha fallat
 Name[ca@valencia]=La conversió de dades del Nepomuk ha fallat
 Name[cs]=Převod dat Nepomuku selhal
@@ -460,6 +473,7 @@ Name[te]=నెపోమక్ దత్తాంశమును మార్ప
 Name[tg]=Nepomuk Data Migration Level 1
 Name[th]=การแปลงข้อมูลของ Nepomuk ล้มเหลว
 Name[tr]=Nepomuk verileri dönüştürülemedi
+Name[ug]=Nepomuk سانلىق-مەلۇماتىنى ئايلاندۇرۇش مەغلۇپ بولدى
 Name[uk]=Спроба перетворення даних Nepomuk завершилася невдало
 Name[wa]=Li coviersaedje des dinêyes Nepomuk a fwait berwete
 Name[x-test]=xxConverting Nepomuk data failedxx
@@ -469,6 +483,8 @@ Comment=Converting Nepomuk data to a new backend failed
 Comment[ar]=فشلت علمية تحويل بيانات نبومك إلى منتهى خلفي جديد
 Comment[ast]=Falló la conversión de datos Nepomuk al nuevu motor
 Comment[be@latin]=Nie ŭdałosia pieraŭtvaryć źviestki „Nepomuk” dla novaha schovišča.
+Comment[bg]=Грешка при преобразуване на данните от Nepomuk към ново ядро за съхранение
+Comment[bs]=Pretvaranje Nepomukovih podataka za novu pozadinu nije uspelo
 Comment[ca]=La conversió de dades del Nepomuk a un dorsal nou ha fallat
 Comment[ca@valencia]=La conversió de dades del Nepomuk a un dorsal nou ha fallat
 Comment[cs]=Převod dat Nepomuku do nového úložného systému selhal
@@ -531,6 +547,7 @@ Comment[ta]=Converting Nepomuk data to a new backend failed
 Comment[te]=నెపోమక్ దత్తాంశ క్షేత్రమును కొత్త బ్యాకెండ్‌కు మార్పిడి చేయుటలో వైఫల్యం
 Comment[th]=การแปลงข้อมูลของ Nepomuk ไปยังแบ็คเอนด์ตัวใหม่ล้มเหลว
 Comment[tr]=Nepomuk verileri yeni arka uca dönüştürülemedi
+Comment[ug]=Nepomuk سانلىق-مەلۇماتىنى يېڭى ئارقا ئۇچقا ئايلاندۇرۇش مەغلۇپ بولدى
 Comment[uk]=Спроба перетворення даних Nepomuk для нового сервера зазнала невдачі
 Comment[wa]=Li coviersaedje des dinêyes Nepomuk viè on novea programe fondmint a fwait berwete
 Comment[x-test]=xxConverting Nepomuk data to a new backend failedxx
@@ -544,6 +561,7 @@ Name[ar]=انتهت عملية تحويل بيانات نبومك
 Name[ast]=Fecha la conversión de datos Nepomuk
 Name[be@latin]=Pieraŭtvareńnie źviestak „Nepomuk” skončanaje.
 Name[bg]=Преобразуването данните на Nepomuk приключи
+Name[bs]=Gotovo pretvaranje Nepomukovih podataka
 Name[ca]=S'ha fet la conversió de dades del Nepomuk
 Name[ca@valencia]=S'ha fet la conversió de dades del Nepomuk
 Name[cs]=Data Nepomuku byla převedena
@@ -607,6 +625,7 @@ Name[te]=నెపోమక్ దత్తాంశం మార్పిడి
 Name[tg]=Nepomuk Data Migration Level 1
 Name[th]=การแปลงข้อมูลของ Nepomuk เสร็จสิ้นแล้ว
 Name[tr]=Nepomuk verileri dönüştürüldü
+Name[ug]=Nepomuk سانلىق-مەلۇماتىنى ئايلاندۇرۇش تامام
 Name[uk]=Перетворення даних Nepomuk завершено
 Name[wa]=Li coviersaedje des dinêyes Nepomukest fwait
 Name[x-test]=xxConverting Nepomuk data donexx
@@ -616,6 +635,8 @@ Comment=Successfully converted Nepomuk data to new backend
 Comment[ar]=حولت بيانات نبومك بنجاح إلى منتهى خلفي جديد
 Comment[ast]=Convirtiéronse correutamente los datos Nepomuk al nuevu motor
 Comment[be@latin]=Pieraŭtvareńnie źviestak „Nepomuk” dla novaj versii paśpiachova skončanaje.
+Comment[bg]=Данните от Nepomuk са успешно преобразувани
+Comment[bs]=Nepomukovi podaci su uspješno pretvoreni za novu pozadinu
 Comment[ca]=Les dades del Nepomuk s'han convertit correctament a un dorsal nou
 Comment[ca@valencia]=Les dades del Nepomuk s'han convertit correctament a un dorsal nou
 Comment[cs]=Data Nepomuku úspěšně převedena do nového úložného systému
@@ -677,6 +698,7 @@ Comment[ta]=Successfully converted Nepomuk data to new backend
 Comment[te]=నెపోమక్ దత్తాంశం కొత్త బ్యాకెండ్‌కు సమర్ధవంతంగా మార్పిడి జరిగింది
 Comment[th]=การแปลงข้อมูลของ Nepomuk ไปยังแบ็คเอนด์ตัวใหม่เสร็จสิ้นแล้ว
 Comment[tr]=Nepomuk verileri yeni arka uca başarılı bir şekilde dönüştürüldü
+Comment[ug]=Nepomuk سانلىق مەلۇماتى مۇۋەپپەقىيەتلىك ھالدا يېڭى ئارقا ئۇچقا ئايلاندۇرۇلدى
 Comment[uk]=Дані Nepomuk успішно перетворено для нового сервера
 Comment[wa]=Dj' a coviersé les dinêyes Nepomuk viè on novea programe fondmint comifåt
 Comment[x-test]=xxSuccessfully converted Nepomuk data to new backendxx
diff --git a/nepomuk/services/storage/ontologyloader.cpp b/nepomuk/services/storage/ontologyloader.cpp
index 729a031..c06a297 100644
--- a/nepomuk/services/storage/ontologyloader.cpp
+++ b/nepomuk/services/storage/ontologyloader.cpp
@@ -20,7 +20,6 @@
 #include "ontologymanagermodel.h"
 #include "ontologymanageradaptor.h"
 #include "graphretriever.h"
-#include "kuvo.h"
 
 #include <Soprano/Global>
 #include <Soprano/Node>
@@ -75,6 +74,24 @@ void Nepomuk::OntologyLoader::Private::updateOntology( const QString& filename )
     // only update if the modification date of the ontology file changed (not the desktop file).
     // ------------------------------------
     QFileInfo ontoFileInf( df.readEntry( QLatin1String("Path") ) );
+
+#ifdef Q_OS_WIN
+    if ( ! ontoFileInf.exists() ) {
+        // On Windows the Path setting is always the install directory which is not
+        // something like /usr/share/ontologies but something like
+        // r:\kderoot\build\shared-desktop-ontologies-mingw-i686\image\shared\ontologies
+        // so if you did not compile shared-desktop-ontologies yourself the Path
+        // is useless.
+        // We expect a default ontologies installation where the .ontology files are placed
+        // in the same directory as the .trig files.
+        QString alt_filename = filename;
+        QFileInfo ontoAlternative( alt_filename.replace( QLatin1String(".ontology"),
+                                                         QLatin1String(".trig") ) );
+        ontoFileInf = ontoAlternative;
+        kDebug() << "Ontology path: " << filename << " does not exist. Using "
+                 << alt_filename << " instead";
+    }
+#endif
     QString ontoNamespace = df.readEntry( QLatin1String("Namespace") );
     QDateTime ontoLastModified = model->ontoModificationDate( ontoNamespace );
     bool update = false;
@@ -83,9 +100,9 @@ void Nepomuk::OntologyLoader::Private::updateOntology( const QString& filename )
         kDebug() << "Ontology" << ontoNamespace << "needs updating.";
         update = true;
     }
-    else {
-        kDebug() << "Ontology" << ontoNamespace << "up to date.";
-    }
+    //else {
+    //    kDebug() << "Ontology" << ontoNamespace << "up to date.";
+    //}
 
     if( !update && forceOntologyUpdate ) {
         kDebug() << "Ontology update forced.";
diff --git a/nepomuk/services/storage/ontologyloader.h b/nepomuk/services/storage/ontologyloader.h
index 901ad43..4e0ce26 100644
--- a/nepomuk/services/storage/ontologyloader.h
+++ b/nepomuk/services/storage/ontologyloader.h
@@ -68,7 +68,7 @@ namespace Nepomuk {
     Q_SIGNALS:
         /**
          * Emitted once the update of the ontologies is done.
-         * This signal is emited whenever the ontologies change
+         * This signal is emitted whenever the ontologies change
          * and needed updating.
          */
         void ontologyLoadingFinished( Nepomuk::OntologyLoader* );
diff --git a/nepomuk/services/storage/ontologymanagermodel.cpp b/nepomuk/services/storage/ontologymanagermodel.cpp
index 63d020b..3ee1cd0 100644
--- a/nepomuk/services/storage/ontologymanagermodel.cpp
+++ b/nepomuk/services/storage/ontologymanagermodel.cpp
@@ -387,6 +387,8 @@ bool Nepomuk::OntologyManagerModel::removeOntology( const QUrl& ns )
         // now removing the ontology is simple
         removeContext( dataGraphUri );
         removeContext( metadataGraphUri );
+        // be sure we remove any junk from buggy versions
+        removeAllStatements( dataGraphUri, Soprano::Node(), Soprano::Node() );
         return true;
     }
     else {
@@ -404,14 +406,14 @@ QDateTime Nepomuk::OntologyManagerModel::ontoModificationDate( const QUrl& uri )
                              "?onto <%1> ?ns . "
                              "?onto <%3> ?date . "
                              "FILTER(STR(?ns) = \"%2\") . "
-                             "FILTER(DATATYPE(?date) = <%4>) . }" )
+                             "FILTER(DATATYPE(?date) = <%4>) . } LIMIT 1" )
                     .arg( Soprano::Vocabulary::NAO::hasDefaultNamespace().toString() )
                     .arg( uri.toString() )
                     .arg( Soprano::Vocabulary::NAO::lastModified().toString() )
                     .arg( Soprano::Vocabulary::XMLSchema::dateTime().toString() );
     QueryResultIterator it = executeQuery( query, Soprano::Query::QueryLanguageSparql );
     if ( it.next() ) {
-        kDebug() << "Found modification date for" << uri << it.binding( "date" ).literal().toDateTime();
+        //kDebug() << "Found modification date for" << uri << it.binding( "date" ).literal().toDateTime();
         return it.binding( "date" ).literal().toDateTime();
     }
     else {
diff --git a/nepomuk/services/storage/removablemediamodel.cpp b/nepomuk/services/storage/removablemediamodel.cpp
new file mode 100644
index 0000000..ebd83f1
--- /dev/null
+++ b/nepomuk/services/storage/removablemediamodel.cpp
@@ -0,0 +1,412 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "removablemediamodel.h"
+#include "removablemediacache.h"
+
+#include <Soprano/Statement>
+#include <Soprano/StatementIterator>
+#include <Soprano/IteratorBackend>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/QueryResultIteratorBackend>
+#include <Soprano/Vocabulary/NAO>
+
+#include <Nepomuk/Vocabulary/NIE>
+#include <Nepomuk/Vocabulary/NFO>
+
+#include <KUrl>
+#include <KDebug>
+
+#include <QtCore/QFile>
+
+
+using namespace Nepomuk::Vocabulary;
+
+// TODO: how do we handle this scenario: the indexer indexes files on a removable medium. This includes
+//       a nie:isPartOf relation to the parent folder of the mount point. This is technically not correct
+//       as it should be part of the removable file system instead. Right?
+
+
+/**
+ * A simple wrapper iterator which converts all filex:/ URLs to local file:/ URLs (if possible).
+ */
+class Nepomuk::RemovableMediaModel::StatementIteratorBackend : public Soprano::IteratorBackend<Soprano::Statement>
+{
+public:
+    StatementIteratorBackend(const RemovableMediaModel* model,
+                             const Soprano::StatementIterator& it)
+        : m_it(it),
+          m_model(model) {
+    }
+
+    bool next() {
+        return m_it.next();
+    }
+
+    Soprano::Statement current() const {
+        return m_model->convertFilexUrls(m_it.current());
+    }
+
+    void close() {
+        m_it.close();
+    }
+
+private:
+    Soprano::StatementIterator m_it;
+    const RemovableMediaModel* m_model;
+};
+
+
+/**
+ * A simple wrapper iterator which converts all filex:/ URLs to local file:/ URLs (if possible).
+ */
+class Nepomuk::RemovableMediaModel::QueryResultIteratorBackend : public Soprano::QueryResultIteratorBackend
+{
+public:
+    QueryResultIteratorBackend(const Nepomuk::RemovableMediaModel* model, const Soprano::QueryResultIterator& it)
+        : m_it(it),
+          m_model(model) {
+    }
+
+    bool next() {
+        return m_it.next();
+    }
+
+    void close() {
+        m_it.close();
+    }
+
+    Soprano::Statement currentStatement() const {
+        return m_model->convertFilexUrls(m_it.currentStatement());
+    }
+
+    Soprano::Node binding( const QString &name ) const {
+        return m_model->convertFilexUrl(m_it.binding(name));
+    }
+
+    Soprano::Node binding( int offset ) const {
+        return m_model->convertFilexUrl(m_it.binding(offset));
+    }
+
+    int bindingCount() const {
+        return m_it.bindingCount();
+    }
+
+    QStringList bindingNames() const {
+        return m_it.bindingNames();
+    }
+
+    bool isGraph() const {
+        return m_it.isGraph();
+    }
+
+    bool isBinding() const {
+        return m_it.isBinding();
+    }
+
+    bool isBool() const {
+        return m_it.isBool();
+    }
+
+    bool boolValue() const {
+        return m_it.boolValue();
+    }
+
+private:
+    Soprano::QueryResultIterator m_it;
+    const RemovableMediaModel* m_model;
+};
+
+
+Nepomuk::RemovableMediaModel::RemovableMediaModel(Soprano::Model* parentModel, QObject* parent)
+    : Soprano::FilterModel(parentModel)
+{
+    setParent(parent);
+    m_removableMediaCache = new RemovableMediaCache(this);
+}
+
+Nepomuk::RemovableMediaModel::~RemovableMediaModel()
+{
+}
+
+Soprano::Error::ErrorCode Nepomuk::RemovableMediaModel::addStatement(const Soprano::Statement &statement)
+{
+        //
+        // First we create the filesystem resource. We mostly need this for the user readable label.
+        //
+//        Nepomuk::Resource fsRes( entry.m_uuid, Nepomuk::Vocabulary::NFO::Filesystem() );
+//        fsRes.setLabel( entry.m_description );
+
+        // IDEA: What about nie:hasPart nfo:FileSystem for all top-level items instead of the nfo:Folder that is the mount point.
+        // But then we run into the recursion problem
+//        Resource fileRes( resource );
+//        fileRes.addProperty( Nepomuk::Vocabulary::NIE::isPartOf(), fsRes );
+
+    return FilterModel::addStatement(convertFileUrls(statement));
+}
+
+Soprano::Error::ErrorCode Nepomuk::RemovableMediaModel::removeStatement(const Soprano::Statement &statement)
+{
+    return FilterModel::removeStatement(convertFileUrls(statement));
+}
+
+Soprano::Error::ErrorCode Nepomuk::RemovableMediaModel::removeAllStatements(const Soprano::Statement &statement)
+{
+    return FilterModel::removeAllStatements(convertFileUrls(statement));
+}
+
+bool Nepomuk::RemovableMediaModel::containsStatement(const Soprano::Statement &statement) const
+{
+    return FilterModel::containsStatement(convertFileUrls(statement));
+}
+
+bool Nepomuk::RemovableMediaModel::containsAnyStatement(const Soprano::Statement &statement) const
+{
+    return FilterModel::containsAnyStatement(convertFileUrls(statement));
+}
+
+Soprano::StatementIterator Nepomuk::RemovableMediaModel::listStatements(const Soprano::Statement &partial) const
+{
+    return new StatementIteratorBackend(this, FilterModel::listStatements(partial));
+}
+
+Soprano::QueryResultIterator Nepomuk::RemovableMediaModel::executeQuery(const QString &query, Soprano::Query::QueryLanguage language, const QString &userQueryLanguage) const
+{
+    return new QueryResultIteratorBackend(this, FilterModel::executeQuery(convertFileUrls(query), language, userQueryLanguage));
+}
+
+Soprano::Node Nepomuk::RemovableMediaModel::convertFileUrl(const Soprano::Node &node) const
+{
+    if(node.isResource()) {
+        const QUrl url = node.uri();
+        if(url.scheme() == QLatin1String("file")) {
+            const QString localFilePath = url.toLocalFile();
+            if(const RemovableMediaCache::Entry* entry = m_removableMediaCache->findEntryByFilePath(localFilePath)) {
+                if(entry->isMounted()) {
+                    return entry->constructRelativeUrl(localFilePath);
+                }
+            }
+        }
+    }
+
+    return node;
+}
+
+
+Soprano::Statement Nepomuk::RemovableMediaModel::convertFileUrls(const Soprano::Statement &statement) const
+{
+    if(statement.predicate().uri() == NIE::url() ||
+            (statement.predicate().isEmpty() && statement.object().isResource())) {
+        Soprano::Statement newStatement(statement);
+        newStatement.setObject(convertFileUrl(statement.object()));
+        return newStatement;
+    }
+
+    return statement;
+}
+
+
+Soprano::Statement Nepomuk::RemovableMediaModel::convertFilexUrls(const Soprano::Statement &s) const
+{
+    if(s.predicate().uri() == NIE::url()) {
+        Soprano::Statement newStatement(s);
+        newStatement.setObject(convertFilexUrl(s.object()));
+        return newStatement;
+    }
+    else {
+        return s;
+    }
+}
+
+Soprano::Node Nepomuk::RemovableMediaModel::convertFilexUrl(const Soprano::Node &node) const
+{
+    if(node.isResource()) {
+        const QUrl url = node.uri();
+        if(m_removableMediaCache->hasRemovableSchema(url)) {
+            if(const RemovableMediaCache::Entry* entry = m_removableMediaCache->findEntryByUrl(url)) {
+                if(entry->isMounted()) {
+                    return QUrl::fromLocalFile(entry->constructLocalPath(url));
+                }
+            }
+        }
+    }
+    // fallback
+    return node;
+}
+
+QString Nepomuk::RemovableMediaModel::convertFileUrls(const QString &query) const
+{
+    //
+    // There are at least two cases to handle:
+    // 1. Simple file:/ URLs used as resources (Example: "<file:///home/foobar>")
+    // 2. REGEX filters which contain partial file:/ URLs (Example: "FILTER(REGEX(STR(?u),'^file:///home'))")
+    //
+    // In theory there are more candidates like matching part of a URL but these have no relevance in Nepomuk.
+    //
+    // We cannot simply match via a regular expression since in theory file URLs could appear in literals
+    // as well. We do not want to change literals.
+    //
+    // Literals are either enclosed in a set of single or double quotes or in two sets of 3 of the same.
+    // Examples: 'Hello', "Hello", '''Hello''', """Hello"""
+    //
+
+    // is 0, 1, or 3 - nothing else
+    int quoteCnt = 0;
+    bool inRegEx = false;
+    bool inRes = false;
+    QChar quote;
+    QString newQuery;
+    for(int i = 0; i < query.length(); ++i) {
+        const QChar c = query[i];
+
+        // first we update the quoteCnt
+        if(c == '\'' || c == '"') {
+           if(quoteCnt == 0) {
+               // opening a literal
+               quote = c;
+               ++quoteCnt;
+               newQuery.append(c);
+               // peek forward to see if there are three
+               if(i+2 < query.length() &&
+                       query[i+1] == c &&
+                       query[i+2] == c) {
+                   quoteCnt = 3;
+                   i += 2;
+                   newQuery.append(c);
+                   newQuery.append(c);
+               }
+               continue;
+           }
+           else if(c == quote) {
+               // possibly closing a literal
+               if(quoteCnt == 1) {
+                   quoteCnt = 0;
+                   newQuery.append(c);
+                   continue;
+               }
+               else { // quoteCnt == 3
+                   // peek forward to see if we have three closing ones
+                   if(i+2 < query.length() &&
+                           query[i+1] == c &&
+                           query[i+2] == c) {
+                       quoteCnt = 0;
+                       i += 2;
+                       newQuery.append(c);
+                       newQuery.append(c);
+                       newQuery.append(c);
+                       continue;
+                   }
+               }
+           }
+        }
+
+        //
+        // If we are not in a quote we can look for a resource
+        //
+        if(!quoteCnt && c == '<') {
+            // peek forward to see if its a file:/ URL
+            if(i+6 < query.length() &&
+                    query[i+1] == 'f' &&
+                    query[i+2] == 'i' &&
+                    query[i+3] == 'l' &&
+                    query[i+4] == 'e' &&
+                    query[i+5] == ':' &&
+                    query[i+6] == '/') {
+                // look for the end of the file URL
+                int pos = query.indexOf('>', i+6);
+                if(pos > i+6) {
+                    // convert the file URL into a filex URL (if necessary)
+                    const KUrl fileUrl = query.mid(i+1, pos-i-1);
+                    newQuery += convertFileUrl(fileUrl).toN3();
+                    i = pos;
+                    continue;
+                }
+            }
+            inRes = true;
+        }
+
+        else if(inRes && c == '>') {
+            inRes = false;
+        }
+
+        //
+        // Or we check for a regex filter
+        //
+        else if(!inRes && !inRegEx && !quoteCnt && c.toLower() == 'r') {
+            // peek forward to see if we have a REGEX filter
+            if(i+4 < query.length() &&
+                    query[i+1].toLower() == 'e' &&
+                    query[i+2].toLower() == 'g' &&
+                    query[i+3].toLower() == 'e' &&
+                    query[i+4].toLower() == 'x') {
+                inRegEx = true;
+            }
+        }
+
+        //
+        // Find the end of a regex.
+        // FIXME: this is a bit tricky. There might be additional brackets in a regex. not sure.
+        //
+        else if(inRegEx && !quoteCnt && c == ')') {
+            inRegEx = false;
+        }
+
+        //
+        // Check for a file URL in a regex. This means we need to be in quotes
+        // and in a regex
+        // This is not perfect as in theory we could have a regex which checks
+        // some random literal which happens to mention a file URL. However,
+        // that case seems unlikely enough for us to go this way.
+        // FIXME: it would be best to check if the filter is done on nie:url.
+        //
+        else if(inRegEx && quoteCnt && c == 'f') {
+            // peek forward to see if its a file URL
+            if(i+5 < query.length() &&
+                    query[i+1] == 'i' &&
+                    query[i+2] == 'l' &&
+                    query[i+3] == 'e' &&
+                    query[i+4] == ':' &&
+                    query[i+5] == '/') {
+                // find end of regex
+                QString quoteEnd = quote;
+                if(quoteCnt == 3) {
+                    quoteEnd += quote;
+                    quoteEnd += quote;
+                }
+                int pos = query.indexOf(quoteEnd, i+6);
+                if(pos > 0) {
+                    // convert the file URL into a filex URL (if necessary)
+                    const KUrl fileUrl = query.mid(i, pos-i);
+                    newQuery += KUrl(convertFileUrl(fileUrl).uri()).url();
+                    // set i to last char we handled and let the loop continue with the end of the quote
+                    i = pos-1;
+                    continue;
+                }
+            }
+        }
+
+        newQuery += c;
+    }
+
+    return newQuery;
+}
+
+#include "removablemediamodel.moc"
diff --git a/nepomuk/services/storage/removablemediamodel.h b/nepomuk/services/storage/removablemediamodel.h
new file mode 100644
index 0000000..52e4eda
--- /dev/null
+++ b/nepomuk/services/storage/removablemediamodel.h
@@ -0,0 +1,114 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef REMOVABLEMEDIAMODEL_H
+#define REMOVABLEMEDIAMODEL_H
+
+#include <Soprano/FilterModel>
+
+#include <QtCore/QVariant>
+#include <QtCore/QHash>
+
+namespace Solid {
+    class StorageAccess;
+    class StorageVolume;
+}
+
+class KUrl;
+
+namespace Nepomuk {
+class RemovableMediaCache;
+
+/**
+ * Filter model that performs automatic conversion of file URLs
+ * from and to the filex:/ protocol.
+ *
+ * The basic idea is that all resources representing files on removable media
+ * do have a nie:url with schema filex:/<UUID>/<relative-path> where <UUID>
+ * is the UUID of the removable media and <relative-path> is the path of the
+ * file on the medium. This is necessary since different media might be mounted
+ * at the same mount point which could lead to clashing URLs.
+ *
+ * In theory all file URLs could be stored this way. However, to improve performance
+ * this conversion is only done for files on removable media.
+ *
+ * The following conversions are performed to provide a handling
+ * of files on removable media that is as transparent as possible:
+ *
+ * \li file:/ URLs used in all statement commands and queries are converted to
+ * the corresponding filex:/ URL if necessary.
+ * \li filex:/ URLs referring to existing files (on a medium currently mounted)
+ * are converted to file:/ URLs in query and listStatement results.
+ * \li filex:/ URLs referring to non-existing files (on a medium currently not
+ * mounted) will not be converted (since it is not possible).
+ *
+ * Thus, whenever clients see a filex:/ URL they can be sure that it refers to
+ * a file that is not accessible at that time and act accordingly (show a message
+ * box, hide the query results, etc.).
+ *
+ * \author Sebastian Trueg <trueg@kde.org>
+ */
+class RemovableMediaModel : public Soprano::FilterModel
+{
+    Q_OBJECT
+
+public:
+    RemovableMediaModel(Soprano::Model *parentModel = 0, QObject* parent = 0);
+    ~RemovableMediaModel();
+
+    // overloaded methods that provide file:/filex: transparent conversion
+    Soprano::Error::ErrorCode addStatement(const Soprano::Statement &statement);
+    Soprano::Error::ErrorCode removeStatement(const Soprano::Statement &statement);
+    Soprano::Error::ErrorCode removeAllStatements(const Soprano::Statement &statement);
+    bool containsStatement(const Soprano::Statement &statement) const;
+    bool containsAnyStatement(const Soprano::Statement &statement) const;
+    Soprano::StatementIterator listStatements(const Soprano::Statement &partial) const;
+    Soprano::QueryResultIterator executeQuery(const QString &query, Soprano::Query::QueryLanguage language, const QString &userQueryLanguage = QString()) const;
+
+    using FilterModel::addStatement;
+    using FilterModel::listStatements;
+
+private:
+    /**
+     * Converts file:/ URLs into their filex:/ counterpart if necessary.
+     * Used in all statement handling methods.
+     */
+    Soprano::Statement convertFileUrls(const Soprano::Statement& s) const;
+
+    Soprano::Node convertFileUrl(const Soprano::Node& node) const;
+
+    /**
+     * Converts file:/ URLs into their filex:/ counterpart if necessary.
+     * This includes a simple handling of REGEX filters.
+     */
+    QString convertFileUrls(const QString& query) const;
+
+    Soprano::Statement convertFilexUrls(const Soprano::Statement& s) const;
+    Soprano::Node convertFilexUrl(const Soprano::Node& node) const;
+
+    RemovableMediaCache* m_removableMediaCache;
+
+    class StatementIteratorBackend;
+    class QueryResultIteratorBackend;
+};
+}
+
+#endif
diff --git a/nepomuk/services/storage/repository.cpp b/nepomuk/services/storage/repository.cpp
index 93698e5..5578c0c 100644
--- a/nepomuk/services/storage/repository.cpp
+++ b/nepomuk/services/storage/repository.cpp
@@ -15,6 +15,11 @@
 #include "repository.h"
 #include "modelcopyjob.h"
 #include "crappyinferencer2.h"
+#include "removablemediamodel.h"
+#include "datamanagementmodel.h"
+#include "datamanagementadaptor.h"
+#include "classandpropertytree.h"
+#include "graphmaintainer.h"
 
 #include <Soprano/Backend>
 #include <Soprano/PluginManager>
@@ -24,6 +29,8 @@
 #include <Soprano/Error/Error>
 #include <Soprano/Vocabulary/RDF>
 #include <Soprano/Util/SignalCacheModel>
+#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
+#include <Soprano/NRLModel>
 
 #include <KStandardDirs>
 #include <KDebug>
@@ -38,6 +45,7 @@
 #include <QtCore/QFile>
 #include <QtCore/QThread>
 #include <QtCore/QCoreApplication>
+#include <QtDBus/QDBusConnection>
 
 
 namespace {
@@ -53,6 +61,7 @@ Nepomuk::Repository::Repository( const QString& name )
       m_state( CLOSED ),
       m_model( 0 ),
       m_inferencer( 0 ),
+      m_removableStorageModel( 0 ),
       m_backend( 0 ),
       m_modelCopyJob( 0 ),
       m_oldStorageBackend( 0 )
@@ -74,6 +83,9 @@ void Nepomuk::Repository::close()
     delete m_inferencer;
     m_inferencer = 0;
 
+    delete m_removableStorageModel;
+    m_removableStorageModel = 0;
+
     delete m_modelCopyJob;
     m_modelCopyJob = 0;
 
@@ -150,7 +162,7 @@ void Nepomuk::Repository::open()
     // remove old pre 4.4 clucene index
     // =================================
     if ( QFile::exists( m_basePath + QLatin1String( "index" ) ) ) {
-        KIO::del( m_basePath + QLatin1String( "index" ) );
+        KIO::del( QString( m_basePath + QLatin1String( "index" ) ) );
     }
 
     // open storage
@@ -166,16 +178,40 @@ void Nepomuk::Repository::open()
 
     kDebug() << "Successfully created new model for repository" << name();
 
+    // Fire up the graph maintainer on the pure data model.
+    // =================================
+    GraphMaintainer* graphMaintainer = new GraphMaintainer(m_model);
+    connect(graphMaintainer, SIGNAL(finished()), graphMaintainer, SLOT(deleteLater()));
+    graphMaintainer->start();
+
+    // create the one class and property tree to be used in the crappy inferencer 2 and in DMS
+    // =================================
+    m_classAndPropertyTree = new Nepomuk::ClassAndPropertyTree(this);
+
+    // create the crappy inference model which handles rdfs:subClassOf only -> we only use this to improve performance of ResourceTypeTerms
+    // =================================
+    m_inferencer = new CrappyInferencer2( m_classAndPropertyTree, m_model );
+
+    // create the RemovableMediaModel which does the transparent handling of removable mounts
+    // =================================
+    m_removableStorageModel = new Nepomuk::RemovableMediaModel(m_inferencer);
+
     // create a SignalCacheModel to make sure no client slows us down by listening to the stupid signals
     // =================================
-    Soprano::Util::SignalCacheModel* scm = new Soprano::Util::SignalCacheModel( m_model );
+    Soprano::Util::SignalCacheModel* scm = new Soprano::Util::SignalCacheModel( m_removableStorageModel );
     scm->setParent(this); // memory management
-    setParentModel( scm );
 
-    // create the crappy inference model which handles rdfs:subClassOf only -> we only use this to improve performance of ResourceTypeTerms
+    // Create the NRLModel which is required by the DMM below
     // =================================
-    m_inferencer = new CrappyInferencer2( scm );
-    setParentModel(m_inferencer);
+    m_nrlModel = new Soprano::NRLModel(scm);
+    m_nrlModel->setParent(this); // memory management
+
+    // create the DataManagementModel on top of everything
+    // =================================
+    m_dataManagementModel = new DataManagementModel(m_classAndPropertyTree, m_nrlModel, this);
+    m_dataManagementAdaptor = new Nepomuk::DataManagementAdaptor(m_dataManagementModel);
+    QDBusConnection::sessionBus().registerObject(QLatin1String("/datamanagement"), m_dataManagementAdaptor, QDBusConnection::ExportScriptableContents);
+    setParentModel(m_dataManagementModel);
 
     // check if we have to convert
     // =================================
@@ -356,6 +392,21 @@ Soprano::BackendSettings Nepomuk::Repository::readVirtuosoSettings() const
 
 void Nepomuk::Repository::updateInference()
 {
+    // the funny way to update the query prefix cache
+    m_nrlModel->setEnableQueryPrefixExpansion(false);
+    m_nrlModel->setEnableQueryPrefixExpansion(true);
+
+    // update the prefixes in the DMS adaptor for script convenience
+    QHash<QString, QString> prefixes;
+    const QHash<QString, QUrl> namespaces = m_nrlModel->queryPrefixes();
+    for(QHash<QString, QUrl>::const_iterator it = namespaces.constBegin();
+        it != namespaces.constEnd(); ++it) {
+        prefixes.insert(it.key(), QString::fromAscii(it.value().toEncoded()));
+    }
+    m_dataManagementAdaptor->setPrefixes(prefixes);
+
+    // update the rest
+    m_classAndPropertyTree->rebuildTree(this);
     m_inferencer->updateInferenceIndex();
     m_inferencer->updateAllResources();
 }
diff --git a/nepomuk/services/storage/repository.h b/nepomuk/services/storage/repository.h
index 2610bd8..e9fb7e5 100644
--- a/nepomuk/services/storage/repository.h
+++ b/nepomuk/services/storage/repository.h
@@ -19,30 +19,43 @@
 #include <QtCore/QMap>
 
 #include <Soprano/BackendSettings>
-#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
-#include <Soprano/NRLModel>
+#include <Soprano/FilterModel>
 
 
 namespace Soprano {
     class Model;
     class Backend;
+    class NRLModel;
 }
 
 class KJob;
 class CrappyInferencer2;
 
 namespace Nepomuk {
-
+    class RemovableMediaModel;
+    class ResourceWatcherModel;
     class ModelCopyJob;
+    class DataManagementModel;
+    class DataManagementAdaptor;
+    class ClassAndPropertyTree;
 
     /**
      * Represents the main Nepomuk model. While it looks as if there could be more than
      * one instance of Repository there is only the one.
      *
-     * Repository is based on NRLModel only for the query prefix expansion feature. It
-     * uses a Soprano::Utils::SignalCacheModel to compact the several statementsAdded()
-     * and statementsRemoved() signals, and uses CrappyInferencer2 to keep rdfs:subClassOf
-     * and nao:userVisible inference up-to-date.
+     * Repository uses a whole stack of Soprano models to provide its functionality. The
+     * following list shows the layering from top to bottom:
+     *
+     * \li The DataManagementModel provides the actual data modification interface. For this
+     *     purpose it is exported via DBus.
+     * \li The Soprano::NRLModel provides query prefix expansion and graph cleanup features
+     *     that are required by the DMM.
+     * \li The Soprano::Utils::SignalCacheModel is used to compact the several statementsAdded()
+     *     and statementsRemoved() signals.
+     * \li RemovableMediaModel is used to automatically convert the URLs of files
+     *     on USB keys, network shares, and so on from and into mount-point independant URLs
+     *     like nfs://<HOST>/<HOST-PATH>/local/path.ext.
+     * \li CrappyInferencer2 keeps rdfs:subClassOf and nao:userVisible inference up-to-date.
      *
      * On construction it checks for and optionally performs conversion from an old repository
      * type (pre-Virtuoso times) and runs CrappyInferencer2::updateAllResources() which is
@@ -50,7 +63,7 @@ namespace Nepomuk {
      *
      * \author Sebastian Trueg <trueg@kde.org>
      */
-    class Repository : public Soprano::NRLModel
+    class Repository : public Soprano::FilterModel
     {
         Q_OBJECT
 
@@ -92,7 +105,12 @@ namespace Nepomuk {
         State m_state;
 
         Soprano::Model* m_model;
+        Nepomuk::ClassAndPropertyTree* m_classAndPropertyTree;
         CrappyInferencer2* m_inferencer;
+        RemovableMediaModel* m_removableStorageModel;
+        DataManagementModel* m_dataManagementModel;
+        Nepomuk::DataManagementAdaptor* m_dataManagementAdaptor;
+        Soprano::NRLModel* m_nrlModel;
         const Soprano::Backend* m_backend;
 
         // only used during opening
diff --git a/nepomuk/services/storage/resourceidentifier.cpp b/nepomuk/services/storage/resourceidentifier.cpp
new file mode 100644
index 0000000..3a988e6
--- /dev/null
+++ b/nepomuk/services/storage/resourceidentifier.cpp
@@ -0,0 +1,154 @@
+/*
+    <one line to give the library's name and an idea of what it does.>
+    Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+#include "resourceidentifier.h"
+#include "syncresource.h"
+#include "classandpropertytree.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QSet>
+
+#include <Soprano/Node>
+#include <Soprano/Model>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/Vocabulary/NAO>
+#include <Soprano/StatementIterator>
+#include <Soprano/NodeIterator>
+#include <Soprano/Vocabulary/RDFS>
+#include <Soprano/Vocabulary/RDF>
+#include <Nepomuk/Vocabulary/NIE>
+
+#include <KDebug>
+
+using namespace Soprano::Vocabulary;
+using namespace Nepomuk::Vocabulary;
+
+namespace {
+    /// used to handle sets and lists of QUrls
+    template<typename T> QStringList resourcesToN3(const T& urls) {
+        QStringList n3;
+        Q_FOREACH(const QUrl& url, urls) {
+            n3 << Soprano::Node::resourceToN3(url);
+        }
+        return n3;
+    }
+}
+
+Nepomuk::ResourceIdentifier::ResourceIdentifier( Nepomuk::StoreIdentificationMode mode,
+                                                 Soprano::Model *model)
+    : Nepomuk::Sync::ResourceIdentifier( model ),
+      m_mode( mode )
+{
+    // Resource Metadata
+    addOptionalProperty( NAO::created() );
+    addOptionalProperty( NAO::lastModified() );
+    addOptionalProperty( NAO::creator() );
+    addOptionalProperty( NAO::userVisible() );
+}
+
+
+bool Nepomuk::ResourceIdentifier::exists(const KUrl& uri)
+{
+    QString query = QString::fromLatin1("ask { %1 ?p ?o . } ").arg( Soprano::Node::resourceToN3(uri) );
+    return model()->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue();
+}
+
+KUrl Nepomuk::ResourceIdentifier::duplicateMatch(const KUrl& origUri,
+                                                 const QSet<KUrl>& matchedUris )
+{
+    Q_UNUSED( origUri );
+    //
+    // We return the uri that has the oldest nao:created
+    // For backwards compatibility we keep in mind that three are resources which do not have nao:created defined.
+    //
+    Soprano::QueryResultIterator it
+            = model()->executeQuery(QString::fromLatin1("select ?r where { ?r %1 ?date . FILTER(?r in (%2)) . } ORDER BY ASC(?date) LIMIT 1")
+                                    .arg(Soprano::Node::resourceToN3(NAO::created()),
+                                         resourcesToN3(matchedUris).join(QLatin1String(","))),
+                                    Soprano::Query::QueryLanguageSparql);
+    if(it.next()) {
+        return it[0].uri();
+    }
+    else {
+        // FIXME: fallback to what? a random one from the set?
+        return KUrl();
+    }
+}
+
+bool Nepomuk::ResourceIdentifier::isIdentifyingProperty(const QUrl& uri)
+{
+    if( uri == NAO::created()
+            || uri == NAO::creator()
+            || uri == NAO::lastModified()
+            || uri == NAO::userVisible() ) {
+        return false;
+    }
+    else {
+        return ClassAndPropertyTree::self()->isIdentifyingProperty(uri);
+    }
+}
+
+
+bool Nepomuk::ResourceIdentifier::runIdentification(const KUrl& uri)
+{
+    if( m_mode == IdentifyNone )
+        return false;
+
+    if( m_mode == IdentifyNew ) {
+        if( exists( uri ) ) {
+            manualIdentification( uri, uri );
+            return true;
+        }
+    }
+
+    //kDebug() << "Identifying : " << uri;
+    //
+    // Check if a uri with the same name exists
+    //
+    if( exists( uri ) ) {
+        manualIdentification( uri, uri );
+        return true;
+    }
+
+    const Sync::SyncResource & res = simpleResource( uri );
+    //kDebug() << res;
+
+    //
+    // Check if a uri with the same nie:url exists
+    //
+    QUrl nieUrl = res.nieUrl();
+    if( !nieUrl.isEmpty() ) {
+        QString query = QString::fromLatin1("select ?r where { ?r %1 %2 . }")
+                        .arg( Soprano::Node::resourceToN3( NIE::url() ),
+                              Soprano::Node::resourceToN3( nieUrl ) );
+        Soprano::QueryResultIterator it = model()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
+        if( it.next() ) {
+            const QUrl newUri = it["r"].uri();
+            kDebug() << uri << " --> " << newUri;
+            manualIdentification( uri, newUri );
+            return true;
+        }
+
+        return false;
+    }
+
+    // Run the normal identification procedure
+    return Sync::ResourceIdentifier::runIdentification( uri );
+}
diff --git a/nepomuk/services/storage/resourceidentifier.h b/nepomuk/services/storage/resourceidentifier.h
new file mode 100644
index 0000000..14d073c
--- /dev/null
+++ b/nepomuk/services/storage/resourceidentifier.h
@@ -0,0 +1,51 @@
+/*
+    <one line to give the library's name and an idea of what it does.>
+    Copyright (C) 2011  Vishesh Handa <handa.vish@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+#ifndef RESOURCEIDENTIFIER_H
+#define RESOURCEIDENTIFIER_H
+
+#include "../backupsync/lib/resourceidentifier.h"
+#include "datamanagement.h"
+
+#include <KUrl>
+
+namespace Nepomuk {
+
+class ResourceIdentifier : public Sync::ResourceIdentifier
+{
+public:
+    ResourceIdentifier( Nepomuk::StoreIdentificationMode mode, Soprano::Model *model);
+
+protected:
+    virtual KUrl duplicateMatch(const KUrl& uri, const QSet< KUrl >& matchedUris );
+    virtual bool runIdentification(const KUrl& uri);
+
+private:
+    bool isIdentifyingProperty( const QUrl& uri );
+
+    /// Returns true if a resource with uri \p uri exists
+    bool exists( const KUrl& uri );
+
+    Nepomuk::StoreIdentificationMode m_mode;
+};
+
+}
+
+#endif // RESOURCEIDENTIFIER_H
diff --git a/nepomuk/services/storage/resourcemerger.cpp b/nepomuk/services/storage/resourcemerger.cpp
new file mode 100644
index 0000000..6d287e3
--- /dev/null
+++ b/nepomuk/services/storage/resourcemerger.cpp
@@ -0,0 +1,962 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2011  Vishesh Handa <handa.vish@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+#include "resourcemerger.h"
+#include "datamanagementmodel.h"
+#include "classandpropertytree.h"
+#include "nepomuktools.h"
+
+#include <Soprano/Vocabulary/NRL>
+#include <Soprano/Vocabulary/RDF>
+#include <Soprano/Vocabulary/RDFS>
+#include <Soprano/Vocabulary/NAO>
+#include <Soprano/Vocabulary/XMLSchema>
+
+#include <Soprano/StatementIterator>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/FilterModel>
+#include <Soprano/NodeIterator>
+#include <Soprano/LiteralValue>
+#include <Soprano/Node>
+
+#include <KDebug>
+#include <Soprano/Graph>
+#include "resourcewatchermanager.h"
+
+using namespace Soprano::Vocabulary;
+
+
+Nepomuk::ResourceMerger::ResourceMerger(Nepomuk::DataManagementModel* model, const QString& app,
+                                        const QHash< QUrl, QVariant >& additionalMetadata,
+                                        const StoreResourcesFlags& flags )
+{
+    m_app = app;
+    m_additionalMetadata = additionalMetadata;
+    m_model = model;
+    m_flags = flags;
+    m_rvm = model->resourceWatcherManager();
+
+    //setModel( m_model );
+
+    // Resource Metadata
+    metadataProperties.reserve( 4 );
+    metadataProperties.insert( NAO::lastModified() );
+    metadataProperties.insert( NAO::userVisible() );
+    metadataProperties.insert( NAO::created() );
+    metadataProperties.insert( NAO::creator() );
+}
+
+
+Nepomuk::ResourceMerger::~ResourceMerger()
+{
+}
+
+void Nepomuk::ResourceMerger::setMappings(const QHash< KUrl, KUrl >& mappings)
+{
+    m_mappings = mappings;
+}
+
+QHash< KUrl, KUrl > Nepomuk::ResourceMerger::mappings() const
+{
+    return m_mappings;
+}
+
+void Nepomuk::ResourceMerger::setAdditionalGraphMetadata(const QHash<QUrl, QVariant>& additionalMetadata)
+{
+    m_additionalMetadata = additionalMetadata;
+}
+
+QHash< QUrl, QVariant > Nepomuk::ResourceMerger::additionalMetadata() const
+{
+    return m_additionalMetadata;
+}
+
+Soprano::Statement Nepomuk::ResourceMerger::resolveStatement(const Soprano::Statement& st)
+{
+    if( !st.isValid() ) {
+        QString error = QString::fromLatin1("Invalid statement encountered");
+        setError( error, Soprano::Error::ErrorInvalidStatement );
+        return Soprano::Statement();
+    }
+
+    Soprano::Node resolvedSubject = resolveMappedNode( st.subject() );
+    if( lastError() )
+        return Soprano::Statement();
+
+    Soprano::Statement newSt( st );
+    newSt.setSubject( resolvedSubject );
+
+    Soprano::Node object = st.object();
+    if( ( object.isResource() && object.uri().scheme() == QLatin1String("nepomuk") ) || object.isBlank() ) {
+        Soprano::Node resolvedObject = resolveMappedNode( object );
+        if( lastError() )
+            return Soprano::Statement();
+        newSt.setObject( resolvedObject );
+    }
+
+    return newSt;
+}
+
+
+bool Nepomuk::ResourceMerger::push(const Soprano::Statement& st)
+{
+    ClassAndPropertyTree *tree = ClassAndPropertyTree::self();
+    if( tree->maxCardinality(  st.predicate().uri() ) == 1 ) {
+        const bool lazy = ( m_flags & LazyCardinalities );
+        const bool overwrite = (m_flags & OverwriteProperties) &&
+        tree->maxCardinality( st.predicate().uri() ) == 1;
+
+        if( lazy || overwrite ) {
+            // FIXME: This may create some empty graphs
+            // Store them somewhere and remove them if they are now empty
+            m_model->removeAllStatements( st.subject(), st.predicate(), Soprano::Node() );
+        }
+    }
+
+    Soprano::Statement statement( st );
+    if( statement.context().isEmpty() )
+        statement.setContext( m_graph );
+
+
+    return m_model->addStatement( statement );
+}
+
+
+QUrl Nepomuk::ResourceMerger::createGraph()
+{
+    return m_model->createGraph( m_app, m_additionalMetadata );
+}
+
+QMultiHash< QUrl, Soprano::Node > Nepomuk::ResourceMerger::getPropertyHashForGraph(const QUrl& graph) const
+{
+    // trueg: this is more a hack than anything else: exclude the inference types
+    // a real solution would either ignore supertypes of nrl:Graph in checkGraphMetadata()
+    // or only check the new metadata for consistency
+    Soprano::QueryResultIterator it
+            = m_model->executeQuery(QString::fromLatin1("select ?p ?o where { graph ?g { %1 ?p ?o . } . FILTER(?g!=<urn:crappyinference2:inferredtriples>) . }")
+                                    .arg(Soprano::Node::resourceToN3(graph)),
+                                    Soprano::Query::QueryLanguageSparql);
+    //Convert to prop hash
+    QMultiHash<QUrl, Soprano::Node> propHash;
+    while(it.next()) {
+        propHash.insert( it["p"].uri(), it["o"] );
+    }
+    return propHash;
+}
+
+
+bool Nepomuk::ResourceMerger::areEqual(const QMultiHash<QUrl, Soprano::Node>& oldPropHash,
+                                       const QMultiHash<QUrl, Soprano::Node>& newPropHash)
+{
+    //
+    // When checking if two graphs are equal, certain stuff needs to be considered
+    //
+    // 1. The nao:created might not be the same
+    // 2. One graph may contain more rdf:types than the other, but still be the same
+    // 3. The newPropHash does not contain the nao:maintainedBy statement
+
+    QSet<QUrl> oldTypes;
+    QSet<QUrl> newTypes;
+
+    QHash< QUrl, Soprano::Node >::const_iterator it = oldPropHash.constBegin();
+    for( ; it != oldPropHash.constEnd(); it++ ) {
+        const QUrl & propUri = it.key();
+        if( propUri == NAO::maintainedBy() || propUri == NAO::created() )
+            continue;
+
+        if( propUri == RDF::type() ) {
+            oldTypes << it.value().uri();
+            continue;
+        }
+
+        //kDebug() << " --> " << it.key() << " " << it.value();
+        if( !newPropHash.contains( it.key(), it.value() ) ) {
+            //kDebug() << "False value : " << newPropHash.value( it.key() );
+            return false;
+        }
+    }
+
+    it = newPropHash.constBegin();
+    for( ; it != newPropHash.constEnd(); it++ ) {
+        const QUrl & propUri = it.key();
+        if( propUri == NAO::maintainedBy() || propUri == NAO::created() )
+            continue;
+
+        if( propUri == RDF::type() ) {
+            newTypes << it.value().uri();
+            continue;
+        }
+
+        //kDebug() << " --> " << it.key() << " " << it.value();
+        if( !oldPropHash.contains( it.key(), it.value() ) ) {
+            //kDebug() << "False value : " << oldPropHash.value( it.key() );
+            return false;
+        }
+    }
+
+    //
+    // Check the types
+    //
+    newTypes << NRL::InstanceBase();
+    if( !containsAllTypes( oldTypes, newTypes ) || !containsAllTypes( newTypes, oldTypes ) )
+        return false;
+
+    // Check nao:maintainedBy
+    it = oldPropHash.find( NAO::maintainedBy() );
+    if( it == oldPropHash.constEnd() )
+        return false;
+
+    if( it.value().uri() != m_model->findApplicationResource(m_app, false) )
+        return false;
+
+    return true;
+}
+
+bool Nepomuk::ResourceMerger::containsAllTypes(const QSet< QUrl >& types, const QSet< QUrl >& masterTypes)
+{
+    ClassAndPropertyTree* tree = m_model->classAndPropertyTree();
+    foreach( const QUrl & type, types ) {
+        if( !masterTypes.contains( type) ) {
+            QSet<QUrl> superTypes = tree->allParents( type );
+            superTypes.intersect(masterTypes);
+            if(superTypes.isEmpty()) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+
+// Graph Merge rules
+// 1. If old graph is of type discardable and new is non-discardable
+//    -> Then update the graph
+// 2. Otherwsie
+//    -> Keep the old graph
+
+QUrl Nepomuk::ResourceMerger::mergeGraphs(const QUrl& oldGraph)
+{
+    //
+    // Check if mergeGraphs has already been called for oldGraph
+    //
+    QHash< QUrl, QUrl >::const_iterator fit = m_graphHash.constFind( oldGraph );
+    if( fit != m_graphHash.constEnd() ) {
+        //kDebug() << "Already merged once, just returning";
+        return fit.value();
+    }
+
+    QMultiHash<QUrl, Soprano::Node> oldPropHash = getPropertyHashForGraph( oldGraph );
+    QMultiHash<QUrl, Soprano::Node> newPropHash = m_additionalMetadataHash;
+
+    // Compare the old and new property hash
+    // If both have the same properties then there is no point in creating a new graph.
+    // vHanda: This check is very expensive. Is it worth it?
+    if( areEqual( oldPropHash, newPropHash ) ) {
+        //kDebug() << "SAME!!";
+        // They are the same - Don't do anything
+        m_graphHash.insert( oldGraph, QUrl() );
+        return QUrl();
+    }
+
+    QMultiHash<QUrl, Soprano::Node> finalPropHash;
+    //
+    // Graph type nrl:DiscardableInstanceBase is a special case.
+    // Only If both the old and new graph contain nrl:DiscardableInstanceBase then
+    // will the new graph also be discardable.
+    //
+    if( oldPropHash.contains( RDF::type(), NRL::DiscardableInstanceBase() ) &&
+        newPropHash.contains( RDF::type(), NRL::DiscardableInstanceBase() ) )
+        finalPropHash.insert( RDF::type(), NRL::DiscardableInstanceBase() );
+
+    oldPropHash.remove( RDF::type(), NRL::DiscardableInstanceBase() );
+    newPropHash.remove( RDF::type(), NRL::DiscardableInstanceBase() );
+
+    finalPropHash.unite( oldPropHash );
+    finalPropHash.unite( newPropHash );
+
+    // Add app uri
+    if( m_appUri.isEmpty() )
+        m_appUri = m_model->findApplicationResource( m_app );
+    if( !finalPropHash.contains( NAO::maintainedBy(), m_appUri ) )
+        finalPropHash.insert( NAO::maintainedBy(), m_appUri );
+
+    //kDebug() << "Creating : " << finalPropHash;
+    QUrl graph = m_model->createGraph( m_app, finalPropHash );
+
+    m_graphHash.insert( oldGraph, graph );
+    return graph;
+}
+
+QMultiHash< QUrl, Soprano::Node > Nepomuk::ResourceMerger::toNodeHash(const QHash< QUrl, QVariant >& hash)
+{
+    QMultiHash<QUrl, Soprano::Node> propHash;
+    ClassAndPropertyTree *tree = ClassAndPropertyTree::self();
+
+    QHash< QUrl, QVariant >::const_iterator it = hash.constBegin();
+    QHash< QUrl, QVariant >::const_iterator constEnd = hash.constEnd();
+    for( ; it != constEnd; ++it ) {
+        Soprano::Node n = tree->variantToNode( it.value(), it.key() );
+        if( tree->lastError() ) {
+            setError( tree->lastError().message() ,tree->lastError().code() );
+            return QMultiHash< QUrl, Soprano::Node >();
+        }
+
+        propHash.insert( it.key(), n );
+    }
+
+    return propHash;
+}
+
+bool Nepomuk::ResourceMerger::checkGraphMetadata(const QMultiHash< QUrl, Soprano::Node >& hash)
+{
+    ClassAndPropertyTree* tree = m_model->classAndPropertyTree();
+
+    QList<QUrl> types;
+    QHash<QUrl, int> propCardinality;
+
+    QHash< QUrl, Soprano::Node >::const_iterator it = hash.constBegin();
+    for( ; it != hash.constEnd(); it++ ) {
+        const QUrl& propUri = it.key();
+        if( propUri == RDF::type() ) {
+            Soprano::Node object = it.value();
+            if( !object.isResource() ) {
+                setError(QString::fromLatin1("rdf:type has resource range. '%1' does not have a resource type.").arg(object.toN3()), Soprano::Error::ErrorInvalidArgument);
+                return false;
+            }
+
+            // All the types should be a sub-type of nrl:Graph
+            // FIXME: there could be multiple types in the old graph from inferencing. all superclasses of nrl:Graph. However, it would still be valid.
+            if( !tree->isChildOf( object.uri(), NRL::Graph() ) ) {
+                setError( QString::fromLatin1("Any rdf:type specified in the additional metadata should be a subclass of nrl:Graph. '%1' is not.").arg(object.uri().toString()),
+                                   Soprano::Error::ErrorInvalidArgument );
+                return false;
+            }
+            types << object.uri();
+        }
+
+        // Save the cardinality of each property
+        QHash< QUrl, int >::iterator propIter = propCardinality.find( propUri );
+        if( propIter == propCardinality.end() ) {
+            propCardinality.insert( propUri, 1 );
+        }
+        else {
+            propIter.value()++;
+        }
+    }
+
+    it = hash.constBegin();
+    for( ; it != hash.constEnd(); it++ ) {
+        const QUrl & propUri = it.key();
+        // Check the cardinality
+        int maxCardinality = tree->maxCardinality( propUri );
+        int curCardinality = propCardinality.value( propUri );
+
+        if( maxCardinality != 0 ) {
+            if( curCardinality > maxCardinality ) {
+                setError( QString::fromLatin1("%1 has a max cardinality of %2").arg(propUri.toString()).arg(maxCardinality), Soprano::Error::ErrorInvalidArgument );
+                return false;
+            }
+        }
+
+        //
+        // Check the domain and range
+        const QUrl domain = tree->propertyDomain( propUri );
+        const QUrl range = tree->propertyRange( propUri );
+
+        // domain
+        if( !domain.isEmpty() && !tree->isChildOf( types, domain ) ) {
+            setError( QString::fromLatin1("%1 has a rdfs:domain of %2").arg( propUri.toString(), domain.toString() ), Soprano::Error::ErrorInvalidArgument);
+            return false;
+        }
+
+        // range
+        if( !range.isEmpty() ) {
+            const Soprano::Node& object = it.value();
+            if( object.isResource() ) {
+                if( !isOfType( object.uri(), range ) ) {
+                    setError( QString::fromLatin1("%1 has a rdfs:range of %2").arg( propUri.toString(), range.toString() ), Soprano::Error::ErrorInvalidArgument);
+                    return false;
+                }
+            }
+            else if( object.isLiteral() ) {
+                const Soprano::LiteralValue lv = object.literal();
+                if( lv.dataTypeUri() != range ) {
+                    setError( QString::fromLatin1("%1 has a rdfs:range of %2").arg( propUri.toString(), range.toString() ), Soprano::Error::ErrorInvalidArgument);
+                    return false;
+                }
+            }
+        } // range
+    }
+
+    //kDebug() << hash;
+    return true;
+}
+
+QUrl Nepomuk::ResourceMerger::createResourceUri()
+{
+    return m_model->createUri( DataManagementModel::ResourceUri );
+}
+
+QUrl Nepomuk::ResourceMerger::createGraphUri()
+{
+    return m_model->createUri( DataManagementModel::GraphUri );
+}
+
+QList< QUrl > Nepomuk::ResourceMerger::existingTypes(const QUrl& uri) const
+{
+    QList<QUrl> types;
+    QList<Soprano::Node> existingTypes = m_model->listStatements( uri, RDF::type(), Soprano::Node() )
+                                                  .iterateObjects().allNodes();
+    foreach( const Soprano::Node & n, existingTypes ) {
+        types << n.uri();
+    }
+    // all resources have rdfs:Resource type by default
+    types << RDFS::Resource();
+
+    return types;
+}
+
+bool Nepomuk::ResourceMerger::isOfType(const Soprano::Node & node, const QUrl& type, const QList<QUrl> & newTypes) const
+{
+    //kDebug() << "Checking " << node << " for type " << type;
+    ClassAndPropertyTree * tree = m_model->classAndPropertyTree();
+
+    QList<QUrl> types( newTypes );
+    if( !node.isBlank() ) {
+        types << existingTypes( node.uri() );
+    }
+    types += newTypes;
+
+    if( types.isEmpty() ) {
+        kDebug() << node << " does not have a type!!";
+        return false;
+    }
+
+    foreach( const QUrl & uri, types ) {
+        if( uri == type || tree->isChildOf( uri, type ) ) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+Soprano::Node Nepomuk::ResourceMerger::resolveMappedNode(const Soprano::Node& node)
+{
+    // Find in mappings
+    const QUrl uri = node.isBlank() ? node.toN3() : node.uri();
+    QHash< KUrl, KUrl >::const_iterator it = m_mappings.constFind( uri );
+    if( it != m_mappings.constEnd() ) {
+        return it.value();
+    }
+
+    // Do not resolve the blank nodes which need to be created
+    if( node.isBlank() )
+        return node;
+
+    if( uri.scheme() == QLatin1String("nepomuk") ) {
+        QString error = QString::fromLatin1("Could not resolve %1. "
+                                            "You cannot create nepomuk uris using this method")
+                        .arg( Soprano::Node::resourceToN3( uri ) );
+        setError( error, Soprano::Error::ErrorInvalidArgument );
+        return Soprano::Node();
+    }
+
+    return node;
+}
+
+Soprano::Node Nepomuk::ResourceMerger::resolveUnmappedNode(const Soprano::Node& node)
+{
+    if( !node.isBlank() )
+        return node;
+
+    QHash< KUrl, KUrl >::const_iterator it = m_mappings.constFind( QUrl(node.toN3()) );
+    if( it != m_mappings.constEnd() ) {
+        return it.value();
+    }
+
+    QUrl newUri = createResourceUri();
+    m_mappings.insert( QUrl(node.toN3()), newUri );
+
+    Soprano::Node dateTime( Soprano::LiteralValue( QDateTime::currentDateTime() ) );
+    m_model->addStatement( newUri, NAO::created(), dateTime, m_graph );
+    m_model->addStatement( newUri, NAO::lastModified(), dateTime, m_graph );
+
+    return newUri;
+}
+
+void Nepomuk::ResourceMerger::resolveBlankNodesInList(QList<Soprano::Statement> *stList)
+{
+    QMutableListIterator<Soprano::Statement> iter( *stList );
+    while( iter.hasNext() ) {
+        Soprano::Statement &st = iter.next();
+
+        st.setSubject( resolveUnmappedNode(st.subject()) );
+        st.setObject( resolveUnmappedNode(st.object()) );
+    }
+}
+
+void Nepomuk::ResourceMerger::removeDuplicatesInList(QList<Soprano::Statement> *stList)
+{
+    QMutableListIterator<Soprano::Statement> it( *stList );
+    while( it.hasNext() ) {
+        const Soprano::Statement &st = it.next();
+        if( st.subject().isBlank() || st.object().isBlank() )
+            continue;
+
+        const QString query = QString::fromLatin1("select ?g where { graph ?g { %1 %2 %3 . } . } LIMIT 1")
+        .arg(st.subject().toN3(),
+             st.predicate().toN3(),
+             st.object().toN3());
+
+        Soprano::QueryResultIterator qit = m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql);
+        if(qit.next()) {
+            const QUrl oldGraph = qit[0].uri();
+            qit.close();
+
+            m_duplicateStatements.insert( oldGraph, st );
+            it.remove();
+        }
+    }
+}
+
+namespace {
+    QUrl getBlankOrResourceUri( const Soprano::Node & n ) {
+        if( n.isResource() ) {
+            return n.uri();
+        }
+        else if( n.isBlank() ) {
+            return QString( QLatin1String("_:") + n.identifier() );
+        }
+        return QUrl();
+    }
+
+    QUrl xsdDuration() {
+        return QUrl( Soprano::Vocabulary::XMLSchema::xsdNamespace().toString() + QLatin1String("duration") );
+    }
+
+    QStringList nodesToN3( const QList<Soprano::Node> &nodes ) {
+        QStringList list;
+        foreach( const Soprano::Node& node, nodes ) {
+            list << node.toN3();
+        }
+        return list;
+    }
+}
+
+/*
+ Rough algorithm -
+
+ - - Validity checks --
+ 1. Check the graph's additional metadata for validity
+ 2. Resolve all the identified uris - Do not create new nodes
+ 3. Check statement validity -
+    a. Max cardinality checks
+    - Take OverwriteProperties and LazyCardinalities into account
+    b. Domain/Range checks
+
+ -- Graph Handling --
+ 4. Get all the statements which already exist in the model, but in a different graph.
+ 5. Create new graphs which are the result of the merging the present graph and the new one
+    a.) In case the old and new graph are the same then forget about those statements
+ 6. Create the main graph
+ 7. Iterate through all the remaining statements and make a list of the resources that
+    we will be modifying
+
+ -- Actual Statement pushing --
+ 8. Create new resources for all the unidentified blank uris
+    - Notify the RWM
+ 9. Push all the statements
+    a.) Push <nao:lastModified, currentDateTime()> for all the resources that will be modified
+    b.) Push all the type statements
+        - Inform the RWM about these new types
+    c.) Push other statements
+        - Inform the RWM
+        - Remove existing statements if OverwriteProperties or LazyCardinalities
+    d.) Update nao:lastModified for all modified resources
+    e.) Push extra metadata statements, so that the specified nao:lastModified, nao:created
+        are taken into considerations
+ 10. You're done!
+
+ */
+bool Nepomuk::ResourceMerger::merge( const Soprano::Graph& stGraph )
+{
+    //
+    // Check if the additional metadata is valid
+    //
+    if( !additionalMetadata().isEmpty() ) {
+        QMultiHash<QUrl, Soprano::Node> additionalMetadata = toNodeHash(m_additionalMetadata);
+        if( lastError() )
+            return false;
+
+        if( !checkGraphMetadata( additionalMetadata ) ) {
+            return false;
+        }
+    }
+
+    //
+    // Resolve all the mapped statements
+    //
+    // FIXME: Use toSet() once 4.7 has released. toSet() is faster, but it requires a newer version
+    //        of Soprano
+    QList<Soprano::Statement> statements = stGraph.toList();
+    QMutableListIterator<Soprano::Statement> sit( statements );
+    while( sit.hasNext() ) {
+        Soprano::Statement &st = sit.next();
+        st = resolveStatement( st );
+        if( lastError() )
+            return false;
+    }
+
+    //
+    // Check the statement metadata
+    //
+
+    /// Maps a resource to all its types
+    QMultiHash<QUrl, QUrl> types;
+
+    /// Maps <sub,pred> pair to all its values
+    QMultiHash<QPair<QUrl,QUrl>, Soprano::Node> cardinality;
+
+    ClassAndPropertyTree * tree = m_model->classAndPropertyTree();
+    //
+    // First separate all the statements predicate rdf:type.
+    // and collect info required to check the types and cardinality
+    //
+    QList<Soprano::Statement> remainingStatements;
+    QList<Soprano::Statement> typeStatements;
+    QList<Soprano::Statement> metadataStatements;
+
+    foreach( const Soprano::Statement & st, statements ) {
+        const QUrl subUri = getBlankOrResourceUri( st.subject() );
+        const QUrl objUri = getBlankOrResourceUri( st.object() );
+
+        const QUrl prop = st.predicate().uri();
+        if( prop == RDF::type() ) {
+            typeStatements << st;
+            types.insert( subUri, objUri );
+            continue;
+        }
+        // we ignore the metadata properties as they will get special
+        // treatment duing the merging
+        else if( metadataProperties.contains( prop ) ) {
+            metadataStatements << st;
+            continue;
+        }
+        else {
+            remainingStatements << st;
+        }
+
+        // Get the cardinality
+        if( tree->maxCardinality( prop ) > 0 ) {
+            QPair<QUrl,QUrl> subPredPair( subUri, st.predicate().uri() );
+            cardinality.insert( subPredPair, st.object() );
+        }
+    }
+
+
+    //
+    // Check the cardinality
+    //
+    QMultiHash<QPair<QUrl,QUrl>, Soprano::Node>::const_iterator cIter = cardinality.constBegin();
+    QMultiHash<QPair<QUrl,QUrl>, Soprano::Node>::const_iterator cIterEnd = cardinality.constEnd();
+    for( ; cIter != cIterEnd; ) {
+        const QPair<QUrl,QUrl> subPredPair = cIter.key();
+        QList<Soprano::Node> objectValues;
+        for( ; cIter != cIterEnd && cIter.key() == subPredPair ; cIter++ ) {
+            objectValues << cIter.value();
+        }
+
+        const QUrl subUri = subPredPair.first;
+        const QUrl propUri = subPredPair.second;
+
+        int maxCardinality = tree->maxCardinality( propUri );
+
+        if( maxCardinality > 0 ) {
+
+            QStringList filterStringList;
+            QStringList objectN3 = nodesToN3( objectValues );
+            foreach( const QString &n3, objectN3 )
+                filterStringList << QString::fromLatin1("?v!=%1").arg( n3 );
+
+            const QString query = QString::fromLatin1("select count(distinct ?v) where {"
+                                                        " %1 %2 ?v ."
+                                                        "FILTER( %3 ) . }")
+                                    .arg( Soprano::Node::resourceToN3( subUri ),
+                                        Soprano::Node::resourceToN3( propUri ),
+                                        filterStringList.join( QLatin1String(" && ") ) );
+
+            int existingCardinality = m_model->executeQuery( query,
+                                                            Soprano::Query::QueryLanguageSparql )
+                                      .iterateBindings(0)
+                                      .allNodes().first().literal().toInt();
+
+            const int newCardinality = objectValues.size() + existingCardinality;
+
+            // TODO: This can be made faster by not calculating all these values when flags are set
+            if( newCardinality > maxCardinality ) {
+                // Special handling for max Cardinality == 1
+                if( maxCardinality == 1 ) {
+                    // If the difference is 1, then that is okay, as the OverwriteProperties flag
+                    // has been set
+                    if( (m_flags & OverwriteProperties) && (newCardinality-maxCardinality) == 1 ) {
+                        continue;
+                    }
+                }
+
+                // The LazyCardinalities flag has been set, we don't care about cardinalities any more
+                if( (m_flags & LazyCardinalities) ) {
+                    continue;
+                }
+
+                // TODO: Maybe list the existing values as well?
+                QString error = QString::fromLatin1("%1 has a max cardinality of %2. Provided "
+                                                    "%3 values - %4")
+                                .arg( propUri.toString(),
+                                      QString::number(maxCardinality),
+                                      QString::number(objectN3.size()),
+                                      objectN3.join(QLatin1String(", ")) );
+                setError( error, Soprano::Error::ErrorInvalidStatement );
+                return false;
+            }
+        }
+    }
+
+    foreach( const Soprano::Statement & st, remainingStatements ) {
+        const QUrl subUri = getBlankOrResourceUri( st.subject() );
+        const QUrl &propUri = st.predicate().uri();
+
+        //
+        // Check for rdfs:domain and rdfs:range
+        //
+
+        QUrl domain = tree->propertyDomain( propUri );
+        QUrl range = tree->propertyRange( propUri );
+
+//        kDebug() << "Domain : " << domain;
+//        kDebug() << "Range : " << range;
+
+        QList<QUrl> subjectNewTypes = types.values( subUri );
+
+        // domain
+        if( !domain.isEmpty() && !isOfType( subUri, domain, subjectNewTypes ) ) {
+            // Error
+            QList<QUrl> allTypes = ( subjectNewTypes + existingTypes(subUri) );
+
+            QString error = QString::fromLatin1("%1 has a rdfs:domain of %2. "
+                                                "%3 only has the following types %4" )
+                            .arg( Soprano::Node::resourceToN3( propUri ),
+                                  Soprano::Node::resourceToN3( domain ),
+                                  Soprano::Node::resourceToN3( subUri ),
+                                  Nepomuk::resourcesToN3( allTypes ).join(", ") );
+            setError( error, Soprano::Error::ErrorInvalidArgument);
+            return false;
+        }
+
+        // range
+        if( !range.isEmpty() ) {
+            if( st.object().isResource() || st.object().isBlank() ) {
+                const QUrl objUri = getBlankOrResourceUri( st.object() );
+                QList<QUrl> objectNewTypes= types.values( objUri );
+
+                if( !isOfType( objUri, range, objectNewTypes ) ) {
+                    // Error
+                    QList<QUrl> allTypes = ( objectNewTypes + existingTypes(objUri) );
+
+                    QString error = QString::fromLatin1("%1 has a rdfs:range of %2. "
+                                                "%3 only has the following types %4" )
+                                    .arg( Soprano::Node::resourceToN3( propUri ),
+                                          Soprano::Node::resourceToN3( range ),
+                                          Soprano::Node::resourceToN3( objUri ),
+                                          resourcesToN3( allTypes ).join(", ") );
+                    setError( error, Soprano::Error::ErrorInvalidArgument );
+                    return false;
+                }
+            }
+            else if( st.object().isLiteral() ) {
+                const Soprano::LiteralValue lv = st.object().literal();
+                // Special handling for xsd:duration
+                if( range == xsdDuration() && lv.isUnsignedInt() ) {
+                    continue;
+                }
+                if( (!lv.isPlain() && lv.dataTypeUri() != range) ||
+                        (lv.isPlain() && range != RDFS::Literal()) ) {
+                    // Error
+                    QString error = QString::fromLatin1("%1 has a rdfs:range of %2. "
+                                                        "Provided %3")
+                                    .arg( Soprano::Node::resourceToN3( propUri ),
+                                          Soprano::Node::resourceToN3( range ),
+                                          Soprano::Node::literalToN3(lv) );
+                    setError( error, Soprano::Error::ErrorInvalidArgument);
+                    return false;
+                }
+            }
+        } // range
+
+    } // foreach
+
+    // The graph is error free.
+
+    //Merge its statements except for the resource metadata statements
+    //QList<Soprano::Statement> mergeStatements = remainingStatements + typeStatements;
+
+    //
+    // Graph Handling
+    //
+    removeDuplicatesInList( &remainingStatements );
+
+    // If the resource exists then all the type statements provided must match
+    // Therefore after this typeStatements, will only contain the types for the new types
+    removeDuplicatesInList( &typeStatements );
+
+
+    //
+    // Create all the graphs
+    //
+    QMutableHashIterator<QUrl, Soprano::Statement> hit( m_duplicateStatements );
+    while( hit.hasNext() ) {
+        hit.next();
+        const QUrl& oldGraph = hit.key();
+
+        const QUrl newGraph = mergeGraphs( oldGraph );
+
+        // The newGraph is invalid when the oldGraph and the newGraph are the same
+        // In that case those statements can just be ignored.
+        if( !newGraph.isValid() ) {
+            hit.remove();
+        }
+    }
+
+    // Create the main graph, if they are any statements to merge
+    if( !remainingStatements.isEmpty() || !typeStatements.isEmpty() ) {
+        m_graph = createGraph();
+    }
+
+    // Count all the modified resources
+    QSet<QUrl> modifiedResources;
+    foreach( const Soprano::Statement & st, remainingStatements ) {
+        if( !st.subject().isBlank() )
+            modifiedResources.insert( st.subject().uri() );
+        //FIXME: Inform RWM about typeAdded()
+    }
+
+    //
+    // Actual statement pushing
+    //
+
+    // Count all the blank nodes
+    /// Maps a blank node with all its types
+    QMultiHash<QUrl, QUrl> typeHash;
+    foreach( const Soprano::Statement& st, typeStatements ) {
+        if( st.subject().isBlank() )
+            typeHash.insert( st.subject().toN3(), st.object().uri() );
+    }
+
+    // Create all the blank nodes
+    resolveBlankNodesInList( &typeStatements );
+    resolveBlankNodesInList( &remainingStatements );
+    resolveBlankNodesInList( &metadataStatements );
+
+    // Push all these statements and get the list of all the modified resource
+    foreach( Soprano::Statement st, typeStatements ) {
+        st.setContext( m_graph );
+        m_model->addStatement( st );
+    }
+
+    foreach( const Soprano::Statement &st, remainingStatements ) {
+        push( st );
+        m_rvm->addStatement( st );
+    }
+
+    // Inform the ResourceWatcherManager of these new types
+    QHash<QUrl, QUrl>::const_iterator typeIt = typeHash.constBegin();
+    QHash<QUrl, QUrl>::const_iterator typeItEnd = typeHash.constEnd();
+    for( ; typeIt != typeItEnd; ) {
+        const QUrl blankUri = typeIt.key();
+        QList<QUrl> types;
+        for( ; typeIt != typeItEnd && typeIt.key() == blankUri ; typeIt++)
+            types << typeIt.value();
+
+        // Get its resource uri
+        const QUrl resUri = m_mappings.value( blankUri );
+        m_rvm->createResource( resUri, types );
+    }
+
+    // Push all the duplicateStatements
+    QHashIterator<QUrl, Soprano::Statement> hashIter( m_duplicateStatements );
+    while( hashIter.hasNext() ) {
+        hashIter.next();
+        Soprano::Statement st = hashIter.value();
+
+        m_model->removeAllStatements( st.subject(), st.predicate(), st.object(), hashIter.key() );
+        const QUrl newGraph( m_graphHash[hashIter.key()] );
+        st.setContext( newGraph );
+
+        // No need to inform the RVM, we're just changing the graph.
+        m_model->addStatement( st );
+    }
+
+    //
+    // Handle Resource metadata
+    //
+
+    // TODO: Maybe inform the RVM about these metadata changes?
+    // First update the mtime of all the modified resources
+    Soprano::Node currentDateTime = Soprano::LiteralValue( QDateTime::currentDateTime() );
+    foreach( const QUrl & resUri, modifiedResources ) {
+        m_model->removeAllStatements( resUri, NAO::lastModified(), Soprano::Node() );
+        m_model->addStatement( resUri, NAO::lastModified(), currentDateTime, m_graph );
+    }
+
+    // then push the individual metadata statements
+    foreach( Soprano::Statement st, metadataStatements ) {
+        addResMetadataStatement( st );
+    }
+
+    return true;
+}
+
+
+Soprano::Error::ErrorCode Nepomuk::ResourceMerger::addResMetadataStatement(const Soprano::Statement& st)
+{
+    const QUrl & predicate = st.predicate().uri();
+
+    // Special handling for nao:lastModified and nao:userVisible: only the latest value is correct
+    if( predicate == NAO::lastModified() ||
+            predicate == NAO::userVisible() ) {
+        m_model->removeAllStatements( st.subject(), st.predicate(), Soprano::Node() );
+    }
+
+    // Special handling for nao:created: only the first value is correct
+    else if( predicate == NAO::created() ) {
+        // If nao:created already exists, then do nothing
+        // FIXME: only write nao:created if we actually create the resource or if it was provided by the client, otherwise drop it.
+        if( m_model->containsAnyStatement( st.subject(), NAO::created(), Soprano::Node() ) )
+            return Soprano::Error::ErrorNone;
+    }
+
+    // Special handling for nao:creator
+    else if( predicate == NAO::creator() ) {
+        // FIXME: handle nao:creator somehow
+    }
+
+    return m_model->addStatement( st );
+}
diff --git a/nepomuk/services/storage/resourcemerger.h b/nepomuk/services/storage/resourcemerger.h
new file mode 100644
index 0000000..be369b3
--- /dev/null
+++ b/nepomuk/services/storage/resourcemerger.h
@@ -0,0 +1,140 @@
+/*
+    <one line to give the library's name and an idea of what it does.>
+    Copyright (C) 2011  Vishesh Handa <handa.vish@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+#ifndef NEPOMUK_DATAMANAGEMENT_RESOURCEMERGER_H
+#define NEPOMUK_DATAMANAGEMENT_RESOURCEMERGER_H
+
+#include <QtCore/QHash>
+#include <QtCore/QVariant>
+#include <QtCore/QUrl>
+#include <QtCore/QSet>
+
+#include <KUrl>
+#include <Soprano/Error/ErrorCache>
+
+#include "datamanagement.h"
+
+namespace Soprano {
+    class Node;
+    class Statement;
+    class Graph;
+}
+
+namespace Nepomuk {
+    class DataManagementModel;
+    class ResourceWatcherManager;
+
+    class ResourceMerger : public Soprano::Error::ErrorCache
+    {
+    public:
+        ResourceMerger( Nepomuk::DataManagementModel * model, const QString & app,
+                        const QHash<QUrl, QVariant>& additionalMetadata,
+                        const StoreResourcesFlags& flags );
+        virtual ~ResourceMerger();
+
+        void setMappings( const QHash<KUrl, KUrl> & mappings );
+        QHash<KUrl, KUrl> mappings() const;
+
+        bool merge(const Soprano::Graph& graph);
+
+        void setAdditionalGraphMetadata( const QHash<QUrl, QVariant>& additionalMetadata );
+        QHash<QUrl, QVariant> additionalMetadata() const;
+
+    private:
+        virtual QUrl createGraph();
+        virtual QUrl createResourceUri();
+        virtual QUrl createGraphUri();
+
+        virtual Soprano::Error::ErrorCode addResMetadataStatement( const Soprano::Statement & st );
+
+        bool push( const Soprano::Statement & st );
+
+        //
+        // Resolution
+        //
+        Soprano::Statement resolveStatement( const Soprano::Statement& st );
+        Soprano::Node resolveMappedNode( const Soprano::Node& node );
+        Soprano::Node resolveUnmappedNode( const Soprano::Node& node );
+
+        /// This modifies the list
+        void resolveBlankNodesInList( QList<Soprano::Statement> *stList );
+
+        /**
+         * Removes all the statements that already exist in the model
+         * and adds them to m_duplicateStatements
+         */
+        void removeDuplicatesInList( QList<Soprano::Statement> *stList );
+        QMultiHash<QUrl, Soprano::Statement> m_duplicateStatements;
+
+        QHash<KUrl, KUrl> m_mappings;
+
+        /// Can set the error
+        QMultiHash<QUrl, Soprano::Node> toNodeHash( const QHash<QUrl, QVariant> &hash );
+
+        /**
+         * Each statement that is being merged and already exists, belongs to a graph. This hash
+         * maps that oldGraph -> newGraph.
+         * The newGraph is generated by mergeGraphs, and contains the metdata from the oldGraph
+         * and the additionalMetadata provided.
+         *
+         * \sa mergeGraphs
+         */
+        QHash<QUrl, QUrl> m_graphHash;
+        QHash<QUrl, Soprano::Node> m_additionalMetadataHash;
+        QHash<QUrl, QVariant> m_additionalMetadata;
+
+        QString m_app;
+        QUrl m_appUri;
+        QUrl m_graph;
+
+        StoreResourcesFlags m_flags;
+        Nepomuk::DataManagementModel * m_model;
+
+        QUrl mergeGraphs( const QUrl& oldGraph );
+
+        QList<QUrl> existingTypes( const QUrl& uri ) const;
+
+        /**
+         * Checks if \p node is of rdf:type \p type.
+         *
+         * \param newTypes contains additional types that should be considered as belonging to \p node
+         */
+        bool isOfType( const Soprano::Node& node, const QUrl& type, const QList<QUrl>& newTypes = QList<QUrl>() ) const;
+
+        QMultiHash<QUrl, Soprano::Node> getPropertyHashForGraph( const QUrl & graph ) const;
+
+        bool checkGraphMetadata( const QMultiHash<QUrl, Soprano::Node> & hash );
+        bool areEqual( const QMultiHash<QUrl, Soprano::Node>& oldPropHash,
+                       const QMultiHash<QUrl, Soprano::Node>& newPropHash );
+
+        /**
+         * Returns true if all the types in \p types are present in \p masterTypes
+         */
+        bool containsAllTypes( const QSet<QUrl>& types, const QSet<QUrl>& masterTypes );
+
+        /// Refers to the properties which are considered as resource metadata
+        QSet<QUrl> metadataProperties;
+
+        ResourceWatcherManager *m_rvm;
+    };
+
+}
+
+#endif // NEPOMUK_DATAMANAGEMENT_RESOURCEMERGER_H
diff --git a/nepomuk/services/storage/resourcewatcherconnection.cpp b/nepomuk/services/storage/resourcewatcherconnection.cpp
new file mode 100644
index 0000000..4378c96
--- /dev/null
+++ b/nepomuk/services/storage/resourcewatcherconnection.cpp
@@ -0,0 +1,70 @@
+/*
+    Copyright (C) 2010-11 Vishesh handa <handa.vish@gmail.com>
+    Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 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 "resourcewatcherconnection.h"
+#include "resourcewatcherconnectionadaptor.h"
+#include "resourcewatchermanager.h"
+
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusObjectPath>
+#include <QtDBus/QDBusServiceWatcher>
+
+Nepomuk::ResourceWatcherConnection::ResourceWatcherConnection( ResourceWatcherManager* parent, bool hasProperties )
+    : QObject( parent ),
+      m_hasProperties( hasProperties ),
+      m_manager(parent)
+{
+}
+
+Nepomuk::ResourceWatcherConnection::~ResourceWatcherConnection()
+{
+    m_manager->removeConnection(this);
+}
+
+bool Nepomuk::ResourceWatcherConnection::hasProperties() const
+{
+    return m_hasProperties;
+}
+
+QDBusObjectPath Nepomuk::ResourceWatcherConnection::registerDBusObject( const QString& dbusClient, int id )
+{
+    // build the dbus object path from the id and register the connection as a Query dbus object
+    new ResourceWatcherConnectionAdaptor( this );
+    const QString dbusObjectPath = QString::fromLatin1( "/resourcewatcher/watch%1" ).arg( id );
+    QDBusConnection::sessionBus().registerObject( dbusObjectPath, this );
+
+    // watch the dbus client for unregistration for auto-cleanup
+    m_serviceWatcher = new QDBusServiceWatcher( dbusClient,
+                                                QDBusConnection::sessionBus(),
+                                                QDBusServiceWatcher::WatchForUnregistration,
+                                                this );
+    connect( m_serviceWatcher, SIGNAL(serviceUnregistered(QString)),
+             this, SLOT(close()) );
+
+    // finally return the dbus object path this connection can be found on
+    return QDBusObjectPath( dbusObjectPath );
+}
+
+void Nepomuk::ResourceWatcherConnection::close()
+{
+    deleteLater();
+}
+
+#include "resourcewatcherconnection.moc"
diff --git a/nepomuk/services/storage/resourcewatcherconnection.h b/nepomuk/services/storage/resourcewatcherconnection.h
new file mode 100644
index 0000000..6100fb4
--- /dev/null
+++ b/nepomuk/services/storage/resourcewatcherconnection.h
@@ -0,0 +1,75 @@
+/*
+    Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+    Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 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.
+*/
+
+
+#ifndef RESOURCEWATCHERCONNECTION_H
+#define RESOURCEWATCHERCONNECTION_H
+
+#include <QtCore/QObject>
+#include <QtCore/QUrl>
+#include <QtDBus/QDBusObjectPath>
+
+class QDBusServiceWatcher;
+
+namespace Nepomuk {
+
+    class ResourceWatcherManager;
+
+    class ResourceWatcherConnection : public QObject
+    {
+        Q_OBJECT
+        Q_CLASSINFO( "D-Bus Interface", "org.kde.nepomuk.ResourceWatcherConnection" )
+
+    public:
+        ResourceWatcherConnection( ResourceWatcherManager* parent, bool hasProperties );
+        ~ResourceWatcherConnection();
+
+    signals:
+        Q_SCRIPTABLE void resourceCreated( const QString & uri, const QStringList& types );
+        Q_SCRIPTABLE void resourceRemoved( const QString & uri, const QStringList& types );
+        Q_SCRIPTABLE void resourceTypeAdded( const QString & resUri, const QString & type );
+        Q_SCRIPTABLE void resourceTypeRemoved( const QString & resUri, const QString & type );
+        Q_SCRIPTABLE void propertyAdded( const QString & resource,
+                                         const QString & property,
+                                         const QDBusVariant & value );
+        Q_SCRIPTABLE void propertyRemoved( const QString & resource,
+                                           const QString & property,
+                                           const QDBusVariant & value );
+
+    public Q_SLOTS:
+        Q_SCRIPTABLE void close();
+
+    public:
+        bool hasProperties() const;
+
+        QDBusObjectPath registerDBusObject(const QString &dbusClient, int id);
+
+    private:
+        QString m_objectPath;
+        bool m_hasProperties;
+
+        ResourceWatcherManager* m_manager;
+        QDBusServiceWatcher* m_serviceWatcher;
+
+        friend class ResourceWatcherManager;
+    };
+
+}
+
+#endif // RESOURCEWATCHERCONNECTION_H
diff --git a/nepomuk/services/storage/resourcewatchermanager.cpp b/nepomuk/services/storage/resourcewatchermanager.cpp
new file mode 100644
index 0000000..6276254
--- /dev/null
+++ b/nepomuk/services/storage/resourcewatchermanager.cpp
@@ -0,0 +1,254 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2011  Vishesh Handa <handa.vish@gmail.com>
+    Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 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 "resourcewatchermanager.h"
+#include "resourcewatcherconnection.h"
+
+#include <Soprano/Statement>
+#include <Soprano/Vocabulary/RDF>
+
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusMessage>
+
+#include <KUrl>
+#include <KDebug>
+
+#include <QtCore/QStringList>
+#include <QtCore/QSet>
+
+
+using namespace Soprano::Vocabulary;
+
+namespace {
+QDBusVariant nodeToVariant(const Soprano::Node& node) {
+    if(node.isResource()) {
+        return QDBusVariant(node.uri());
+    }
+    else {
+        return QDBusVariant(node.literal().variant());
+    }
+}
+
+QStringList convertUris(const QList<QUrl>& uris) {
+    QStringList sl;
+    foreach(const QUrl& uri, uris)
+        sl << KUrl(uri).url();
+    return sl;
+}
+
+QList<QUrl> convertUris(const QStringList& uris) {
+    QList<QUrl> sl;
+    foreach(const QString& uri, uris)
+        sl << KUrl(uri);
+    return sl;
+}
+}
+
+Nepomuk::ResourceWatcherManager::ResourceWatcherManager(QObject* parent)
+    : QObject(parent),
+      m_connectionCount(0)
+{
+    QDBusConnection::sessionBus().registerObject("/resourcewatcher", this, QDBusConnection::ExportScriptableSlots);
+}
+
+Nepomuk::ResourceWatcherManager::~ResourceWatcherManager()
+{
+    // the connections call removeConnection() from their descrutors. Thus,
+    // we need to clean them up before we are deleted ourselves
+    QSet<ResourceWatcherConnection*> allConnections
+            = QSet<ResourceWatcherConnection*>::fromList(m_resHash.values())
+            + QSet<ResourceWatcherConnection*>::fromList(m_propHash.values())
+            + QSet<ResourceWatcherConnection*>::fromList(m_typeHash.values());
+    qDeleteAll(allConnections);
+}
+
+
+void Nepomuk::ResourceWatcherManager::addStatement(const Soprano::Statement& st)
+{
+    addProperty( st.subject(), st.predicate().uri(), st.object() );
+}
+
+void Nepomuk::ResourceWatcherManager::addProperty(const Soprano::Node res, const QUrl& property, const Soprano::Node& value)
+{
+    typedef ResourceWatcherConnection RWC;
+
+    // FIXME: take care of duplicate signals!
+
+    //
+    // Emit signals for all the connections that are only watching specific resources
+    //
+    QSet<RWC*> resConnections;
+    QList<RWC*> connections = m_resHash.values( res.uri() );
+    foreach( RWC* con, connections ) {
+        if( !con->hasProperties() ) {
+            emit con->propertyAdded( KUrl(res.uri()).url(),
+                                     property.toString(),
+                                     nodeToVariant(value) );
+        }
+        else {
+            resConnections << con;
+        }
+    }
+
+    //
+    // Emit signals for the connections that are watching specific resources and properties
+    //
+    QList<RWC*> propConnections = m_propHash.values( property );
+    foreach( RWC* con, propConnections ) {
+        QSet<RWC*>::const_iterator it = resConnections.constFind( con );
+        if( it != resConnections.constEnd() ) {
+            emit con->propertyAdded( KUrl(res.uri()).url(),
+                                     property.toString(),
+                                     nodeToVariant(value) );
+        }
+    }
+
+    //
+    // Emit type + property signals
+    //
+    //TODO: Implement me! ( How? )
+}
+
+void Nepomuk::ResourceWatcherManager::removeProperty(const Soprano::Node res, const QUrl& property, const Soprano::Node& value)
+{
+    typedef ResourceWatcherConnection RWC;
+
+    //
+    // Emit signals for all the connections that are only watching specific resources
+    //
+    QSet<RWC*> resConnections;
+    QList<RWC*> connections = m_resHash.values( res.uri() );
+    foreach( RWC* con, connections ) {
+        if( !con->hasProperties() ) {
+            emit con->propertyRemoved( KUrl(res.uri()).url(),
+                                       property.toString(),
+                                       nodeToVariant(value) );
+        }
+        else {
+            resConnections << con;
+        }
+    }
+
+    //
+    // Emit signals for the conn2ections that are watching specific resources and properties
+    //
+    QList<RWC*> propConnections = m_propHash.values( property );
+    foreach( RWC* con, propConnections ) {
+        QSet<RWC*>::const_iterator it = resConnections.constFind( con );
+        if( it != resConnections.constEnd() ) {
+            emit con->propertyRemoved( KUrl(res.uri()).url(),
+                                       property.toString(),
+                                       nodeToVariant(value) );
+        }
+    }
+}
+
+void Nepomuk::ResourceWatcherManager::createResource(const QUrl &uri, const QList<QUrl> &types)
+{
+    QSet<ResourceWatcherConnection*> connections;
+    foreach(const QUrl& type, types) {
+        foreach(ResourceWatcherConnection* con, m_typeHash.values( type )) {
+            connections += con;
+        }
+    }
+
+    foreach(ResourceWatcherConnection* con, connections) {
+        emit con->resourceCreated(KUrl(uri).url(), convertUris(types));
+    }
+}
+
+void Nepomuk::ResourceWatcherManager::removeResource(const QUrl &res, const QList<QUrl>& types)
+{
+    QSet<ResourceWatcherConnection*> connections;
+    foreach(const QUrl& type, types) {
+        foreach(ResourceWatcherConnection* con, m_typeHash.values( type )) {
+            connections += con;
+        }
+    }
+    foreach(ResourceWatcherConnection* con, m_resHash.values( res )) {
+        connections += con;
+    }
+
+    foreach(ResourceWatcherConnection* con, connections) {
+        emit con->resourceRemoved(KUrl(res).url(), convertUris(types));
+    }
+}
+
+Nepomuk::ResourceWatcherConnection* Nepomuk::ResourceWatcherManager::createConnection(const QList<QUrl> &resources,
+                                                                                      const QList<QUrl> &properties,
+                                                                                      const QList<QUrl> &types)
+{
+    kDebug() << resources << properties << types;
+
+    if( resources.isEmpty() && properties.isEmpty() && types.isEmpty() ) {
+        return 0;
+    }
+
+    ResourceWatcherConnection* con = new ResourceWatcherConnection( this, !properties.isEmpty() );
+    foreach( const QUrl& res, resources ) {
+        m_resHash.insert(res, con);
+    }
+
+    foreach( const QUrl& prop, properties ) {
+        m_propHash.insert(prop, con);
+    }
+
+    foreach( const QUrl& type, types ) {
+        m_typeHash.insert(type, con);
+    }
+
+    return con;
+}
+
+QDBusObjectPath Nepomuk::ResourceWatcherManager::watch(const QStringList& resources,
+                                                       const QStringList& properties,
+                                                       const QStringList& types)
+{
+    kDebug() << resources << properties << types;
+
+    if(ResourceWatcherConnection* con = createConnection(convertUris(resources), convertUris(properties), convertUris(types))) {
+        return con->registerDBusObject(message().service(), ++m_connectionCount);
+    }
+    else {
+        return QDBusObjectPath();
+    }
+}
+
+namespace {
+    void removeConnectionFromHash( QMultiHash<QUrl, Nepomuk::ResourceWatcherConnection*> & hash,
+                 const Nepomuk::ResourceWatcherConnection * con )
+    {
+        QMutableHashIterator<QUrl, Nepomuk::ResourceWatcherConnection*> it( hash );
+        while( it.hasNext() ) {
+            if( it.next().value() == con )
+                it.remove();
+        }
+    }
+}
+
+void Nepomuk::ResourceWatcherManager::removeConnection(Nepomuk::ResourceWatcherConnection *con)
+{
+    removeConnectionFromHash( m_resHash, con );
+    removeConnectionFromHash( m_propHash, con );
+    removeConnectionFromHash( m_typeHash, con );
+}
+
+#include "resourcewatchermanager.moc"
diff --git a/nepomuk/services/storage/resourcewatchermanager.h b/nepomuk/services/storage/resourcewatchermanager.h
new file mode 100644
index 0000000..707ddab
--- /dev/null
+++ b/nepomuk/services/storage/resourcewatchermanager.h
@@ -0,0 +1,82 @@
+/*
+    This file is part of the Nepomuk KDE project.
+    Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+    Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 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.
+*/
+
+
+#ifndef RESOURCEWATCHMANAGER_H
+#define RESOURCEWATCHMANAGER_H
+
+#include <QtCore/QMultiHash>
+#include <QtCore/QSet>
+
+#include <Soprano/FilterModel>
+#include <QtDBus/QDBusObjectPath>
+#include <QtDBus/QDBusContext>
+
+namespace Nepomuk {
+
+    class ResourceWatcherConnection;
+
+    class ResourceWatcherManager : public QObject, protected QDBusContext
+    {
+        Q_OBJECT
+        Q_CLASSINFO( "D-Bus Interface", "org.kde.nepomuk.ResourceWatcher" )
+
+    public:
+        ResourceWatcherManager( QObject* parent = 0 );
+        ~ResourceWatcherManager();
+
+        void addStatement(const Soprano::Statement &st);
+        void addProperty(const Soprano::Node res, const QUrl& property, const Soprano::Node& value);
+        void removeProperty(const Soprano::Node res, const QUrl& property, const Soprano::Node& value);
+        void createResource(const QUrl& uri, const QList<QUrl>& types);
+        void removeResource(const QUrl& uri, const QList<QUrl>& types);
+
+    public slots:
+        /**
+         * Used internally by watch() and by the unit tests to create watcher connections.
+         */
+        ResourceWatcherConnection* createConnection(const QList<QUrl>& resources,
+                                                    const QList<QUrl>& properties,
+                                                    const QList<QUrl>& types );
+
+        /**
+         * The main DBus methods exposed by the ResourceWatcher
+         */
+        Q_SCRIPTABLE QDBusObjectPath watch( const QStringList& resources,
+                                            const QStringList& properties,
+                                            const QStringList& types );
+
+    private:
+        /// called by ResourceWatcherConnection destructor
+        void removeConnection(ResourceWatcherConnection*);
+
+        QMultiHash<QUrl, ResourceWatcherConnection*> m_resHash;
+        QMultiHash<QUrl, ResourceWatcherConnection*> m_propHash;
+        QMultiHash<QUrl, ResourceWatcherConnection*> m_typeHash;
+
+        // only used to generate unique dbus paths
+        int m_connectionCount;
+
+        friend class ResourceWatcherConnection;
+    };
+
+}
+
+#endif // RESOURCEWATCHMANAGER_H
diff --git a/nepomuk/services/storage/storage.cpp b/nepomuk/services/storage/storage.cpp
index af29526..9bb295e 100644
--- a/nepomuk/services/storage/storage.cpp
+++ b/nepomuk/services/storage/storage.cpp
@@ -38,8 +38,12 @@ NEPOMUK_EXPORT_SERVICE( Nepomuk::Storage, "nepomukstorage" )
 Nepomuk::Storage::Storage( QObject* parent, const QList<QVariant>& )
     : Service( parent, true /* delayed initialization */ )
 {
+    // register the fancier name for this important service
     QDBusConnection::sessionBus().registerService( "org.kde.NepomukStorage" );
 
+    // TODO: remove this one
+    QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.nepomuk.DataManagement"));
+
     m_core = new Core( this );
     connect( m_core, SIGNAL( initializationDone(bool) ),
              this, SLOT( slotNepomukCoreInitialized(bool) ) );
diff --git a/nepomuk/services/storage/test/CMakeLists.txt b/nepomuk/services/storage/test/CMakeLists.txt
index bc65f1c..e8490ed 100644
--- a/nepomuk/services/storage/test/CMakeLists.txt
+++ b/nepomuk/services/storage/test/CMakeLists.txt
@@ -1,18 +1,156 @@
 set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
 
+include_directories(
+  ${SOPRANO_INCLUDE_DIR}
+  ${CMAKE_SOURCE_DIR}
+  ${NEPOMUK_INCLUDE_DIR}
+  ${CMAKE_CURRENT_BINARY_DIR}/..
+  ${CMAKE_CURRENT_SOURCE_DIR}/..
+  )
+
 kde4_add_unit_test(crappyinferencer2test
   crappyinferencer2test.cpp
   ../crappyinferencer2.cpp
-  ../typevisibilitytree.cpp)
+  ../classandpropertytree.cpp)
 target_link_libraries(crappyinferencer2test
   ${QT_QTTEST_LIBRARY}
   ${SOPRANO_LIBRARIES}
-  ${KDE4_KDECORE_LIBS})
+  ${KDE4_KDECORE_LIBS}
+  nepomukdatamanagement)
+
+kde4_add_unit_test(removablemediamodeltest
+  removablemediamodeltest.cpp
+  ../removablemediamodel.cpp
+)
+add_definitions(-DFAKE_COMPUTER_XML="\\"${CMAKE_CURRENT_SOURCE_DIR}/solid/fakecomputer.xml\\"")
+target_link_libraries(removablemediamodeltest
+  ${QT_QTTEST_LIBRARY}
+  ${SOPRANO_LIBRARIES}
+  ${KDE4_KDECORE_LIBS}
+  ${KDE4_SOLID_LIBS}
+  ${NEPOMUK_LIBRARIES}
+  nepomukcommon
+)
+
+set( DMTEST
+  ../classandpropertytree.cpp
+  ../datamanagementmodel.cpp
+  ../resourcewatchermanager.cpp
+  ../resourcewatcherconnection.cpp
+  ../datamanagementadaptor.cpp
+  ../datamanagementcommand.cpp
+  ../resourcemerger.cpp
+  ../resourceidentifier.cpp
+  qtest_dms.cpp
+)
+
+qt4_add_dbus_adaptor(DMTEST
+  ../../../interfaces/org.kde.nepomuk.ResourceWatcherConnection.xml
+  resourcewatcherconnection.h
+  Nepomuk::ResourceWatcherConnection)
+
+
+kde4_add_library( datamanagementtestlib STATIC ${DMTEST} )
+
+target_link_libraries( datamanagementtestlib
+  ${SOPRANO_LIBRARIES}
+  ${KDE4_KIO_LIBS}
+  ${KDE4_SOLID_LIBS}
+  ${NEPOMUK_LIBRARIES}
+  nepomukdatamanagement
+  nepomuksync
+)
+
+kde4_add_unit_test(classandpropertytreetest
+  classandpropertytreetest.cpp
+)
+target_link_libraries(classandpropertytreetest
+  ${QT_QTTEST_LIBRARY}
+  ${SOPRANO_LIBRARIES}
+  ${KDE4_KDECORE_LIBS}
+  nepomukdatamanagement
+  datamanagementtestlib
+)
+
+kde4_add_unit_test(datamanagementmodeltest
+  datamanagementmodeltest.cpp
+)
+
+target_link_libraries(datamanagementmodeltest
+  ${QT_QTTEST_LIBRARY}
+  ${SOPRANO_LIBRARIES}
+  ${KDE4_KIO_LIBS}
+  ${KDE4_SOLID_LIBS}
+  ${NEPOMUK_LIBRARIES}
+  nepomuksync
+  nepomukdatamanagement
+  datamanagementtestlib
+)
+
+
+kde4_add_unit_test(datamanagementadaptortest
+  datamanagementadaptortest.cpp
+)
+
+target_link_libraries(datamanagementadaptortest
+  ${QT_QTTEST_LIBRARY}
+  ${SOPRANO_LIBRARIES}
+  ${KDE4_KDECORE_LIBS}
+  ${KDE4_KIO_LIBS}
+  ${KDE4_SOLID_LIBS}
+  ${NEPOMUK_LIBRARIES}
+  nepomuksync
+  nepomukdatamanagement
+  datamanagementtestlib
+)
+
+
+configure_file(nepomuk_dms_test_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/nepomuk_dms_test_config.h)
+
+kde4_add_executable(fakedms
+  fakedatamanagementservice.cpp
+)
+
+target_link_libraries(fakedms
+  ${QT_QTTEST_LIBRARY}
+  ${SOPRANO_LIBRARIES}
+  ${SOPRANO_SERVER_LIBRARIES}
+  ${KDE4_KDECORE_LIBS}
+  ${KDE4_KIO_LIBS}
+  ${KDE4_SOLID_LIBS}
+  ${NEPOMUK_LIBRARIES}
+  nepomuksync
+  nepomukdatamanagement
+  datamanagementtestlib
+)
+
+
+kde4_add_unit_test(asyncclientapitest
+  asyncclientapitest.cpp
+)
+
+target_link_libraries(asyncclientapitest
+  ${QT_QTTEST_LIBRARY}
+  ${SOPRANO_LIBRARIES}
+  ${SOPRANO_CLIENT_LIBRARIES}
+  ${KDE4_KDECORE_LIBS}
+  ${NEPOMUK_LIBRARIES}
+  nepomukdatamanagement
+)
+
+kde4_add_executable(resourcewatchertest
+  resourcewatchertest.cpp
+)
 
-kde4_add_unit_test(typevisibilitytreetest
-  typevisibilitytreetest.cpp
-  ../typevisibilitytree.cpp)
-target_link_libraries(typevisibilitytreetest
+target_link_libraries(resourcewatchertest
   ${QT_QTTEST_LIBRARY}
   ${SOPRANO_LIBRARIES}
-  ${KDE4_KDECORE_LIBS})
+  ${SOPRANO_SERVER_LIBRARIES}
+  ${KDE4_KDECORE_LIBS}
+  ${KDE4_KIO_LIBS}
+  ${KDE4_SOLID_LIBS}
+  ${NEPOMUK_LIBRARIES}
+  nepomuksync
+  nepomukdatamanagement
+  datamanagementtestlib
+)
diff --git a/nepomuk/services/storage/test/asyncclientapitest.cpp b/nepomuk/services/storage/test/asyncclientapitest.cpp
new file mode 100644
index 0000000..c430f06
--- /dev/null
+++ b/nepomuk/services/storage/test/asyncclientapitest.cpp
@@ -0,0 +1,471 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "asyncclientapitest.h"
+#include "../datamanagementmodel.h"
+#include "../datamanagementadaptor.h"
+#include "../classandpropertytree.h"
+#include "simpleresource.h"
+#include "simpleresourcegraph.h"
+#include "datamanagement.h"
+#include "createresourcejob.h"
+#include "describeresourcesjob.h"
+#include "nepomuk_dms_test_config.h"
+
+#include <QtTest>
+#include "qtest_kde.h"
+
+#include <QtDBus>
+#include <QProcess>
+#include <Soprano/Soprano>
+#include <Soprano/Client/DBusModel>
+
+#include <Soprano/Graph>
+#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
+#include <Soprano/NRLModel>
+
+#include <ktempdir.h>
+#include <KDebug>
+#include <KJob>
+
+#include <Nepomuk/Vocabulary/NFO>
+#include <Nepomuk/Vocabulary/NMM>
+#include <Nepomuk/Vocabulary/NCO>
+#include <Nepomuk/Vocabulary/NIE>
+
+using namespace Soprano;
+using namespace Soprano::Vocabulary;
+using namespace Nepomuk;
+using namespace Nepomuk::Vocabulary;
+
+
+void AsyncClientApiTest::initTestCase()
+{
+    kDebug() << "Starting fake DMS:" << FAKEDMS_BIN;
+
+    // setup the service watcher so we know when the fake DMS is up
+    QDBusServiceWatcher watcher(QLatin1String("org.kde.nepomuk.FakeDataManagement"),
+                                QDBusConnection::sessionBus(),
+                                QDBusServiceWatcher::WatchForRegistration);
+
+    // start the fake DMS
+    m_fakeDms = new QProcess();
+    m_fakeDms->setProcessChannelMode(QProcess::ForwardedChannels);
+    m_fakeDms->start(QLatin1String(FAKEDMS_BIN));
+
+    // wait for it to come up
+    QTest::kWaitForSignal(&watcher, SIGNAL(serviceRegistered(QString)));
+
+    // get us access to the fake DMS's model
+    m_model = new Soprano::Client::DBusModel(QLatin1String("org.kde.nepomuk.FakeDataManagement"), QLatin1String("/model"));
+
+    qputenv("NEPOMUK_FAKE_DMS_DBUS_SERVICE", "org.kde.nepomuk.FakeDataManagement");
+}
+
+void AsyncClientApiTest::cleanupTestCase()
+{
+    kDebug() << "Shutting down fake DMS...";
+    QDBusInterface(QLatin1String("org.kde.nepomuk.FakeDataManagement"),
+                   QLatin1String("/MainApplication"),
+                   QLatin1String("org.kde.KApplication"),
+                   QDBusConnection::sessionBus()).call(QLatin1String("quit"));
+    m_fakeDms->waitForFinished();
+    delete m_fakeDms;
+    delete m_model;
+}
+
+void AsyncClientApiTest::resetModel()
+{
+    // remove all the junk from previous tests
+    m_model->removeAllStatements();
+
+    // add some classes and properties
+    QUrl graph("graph:/onto");
+    m_model->addStatement( graph, RDF::type(), NRL::Ontology(), graph );
+    // removeResources depends on type inference
+    m_model->addStatement( graph, RDF::type(), NRL::Graph(), graph );
+
+    m_model->addStatement( QUrl("prop:/int"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/int"), RDFS::range(), XMLSchema::xsdInt(), graph );
+
+    m_model->addStatement( QUrl("prop:/int2"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/int2"), RDFS::range(), XMLSchema::xsdInt(), graph );
+
+    m_model->addStatement( QUrl("prop:/int3"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/int3"), RDFS::range(), XMLSchema::xsdInt(), graph );
+
+    m_model->addStatement( QUrl("prop:/int_c1"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/int_c1"), RDFS::range(), XMLSchema::xsdInt(), graph );
+    m_model->addStatement( QUrl("prop:/int_c1"), NRL::maxCardinality(), LiteralValue(1), graph );
+
+    m_model->addStatement( QUrl("prop:/string"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/string"), RDFS::range(), XMLSchema::string(), graph );
+
+    m_model->addStatement( QUrl("prop:/res"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/res"), RDFS::range(), RDFS::Resource(), graph );
+
+    m_model->addStatement( QUrl("prop:/res_c1"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/res_c1"), RDFS::range(), RDFS::Resource(), graph );
+    m_model->addStatement( QUrl("prop:/res_c1"), NRL::maxCardinality(), LiteralValue(1), graph );
+
+    m_model->addStatement( QUrl("prop:/date"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/date"), RDFS::range(), XMLSchema::date(), graph );
+
+    m_model->addStatement( QUrl("prop:/time"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/time"), RDFS::range(), XMLSchema::time(), graph );
+
+    m_model->addStatement( QUrl("prop:/dateTime"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/dateTime"), RDFS::range(), XMLSchema::dateTime(), graph );
+
+    m_model->addStatement( QUrl("class:/A"), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( QUrl("class:/B"), RDF::type(), RDFS::Class(), graph );
+
+    // properties used all the time
+    m_model->addStatement( NAO::identifier(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( RDF::type(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( RDF::type(), RDFS::range(), RDFS::Class(), graph );
+    m_model->addStatement( NIE::url(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NIE::url(), RDFS::range(), RDFS::Resource(), graph );
+
+    // some ontology things the ResourceMerger depends on
+    m_model->addStatement( RDFS::Class(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( RDFS::Class(), RDFS::subClassOf(), RDFS::Resource(), graph );
+    m_model->addStatement( NRL::Graph(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NRL::InstanceBase(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NRL::InstanceBase(), RDFS::subClassOf(), NRL::Graph(), graph );
+    m_model->addStatement( NAO::prefLabel(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NAO::prefLabel(), RDFS::range(), RDFS::Literal(), graph );
+    m_model->addStatement( NFO::fileName(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NFO::fileName(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NCO::fullname(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NCO::fullname(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NIE::title(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NIE::title(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NAO::created(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NAO::created(), RDFS::range(), XMLSchema::dateTime(), graph );
+    m_model->addStatement( NAO::created(), NRL::maxCardinality(), LiteralValue(1), graph );
+    m_model->addStatement( NAO::lastModified(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NAO::lastModified(), RDFS::range(), XMLSchema::dateTime(), graph );
+    m_model->addStatement( NAO::lastModified(), NRL::maxCardinality(), LiteralValue(1), graph );
+
+    // rebuild the internals of the data management model
+    QDBusInterface(QLatin1String("org.kde.nepomuk.FakeDataManagement"),
+                   QLatin1String("/fakedms"),
+                   QLatin1String("org.kde.nepomuk.FakeDataManagement"),
+                   QDBusConnection::sessionBus()).call(QLatin1String("updateClassAndPropertyTree"));
+}
+
+void AsyncClientApiTest::init()
+{
+    resetModel();
+}
+
+
+void AsyncClientApiTest::testAddProperty()
+{
+    KJob* job = Nepomuk::addProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/int"), QVariantList() << 42);
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42)));
+}
+
+void AsyncClientApiTest::testSetProperty()
+{
+    KJob* job = Nepomuk::setProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/int"), QVariantList() << 42);
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42)));
+}
+
+void AsyncClientApiTest::testRemoveProperties()
+{
+    Soprano::NRLModel nrlModel(m_model);
+    const QUrl g1 = nrlModel.createGraph(NRL::InstanceBase());
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(2), g1);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(2), g1);
+
+    KJob* job = Nepomuk::removeProperties(QList<QUrl>() << QUrl("res:/A"), QList<QUrl>() << QUrl("prop:/int") << QUrl("prop:/int2"));
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int"), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int2"), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+}
+
+void AsyncClientApiTest::testCreateResource()
+{
+    CreateResourceJob* job = Nepomuk::createResource(QList<QUrl>() << QUrl("class:/A") << QUrl("class:/B"), QLatin1String("label"), QLatin1String("desc"));
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    const QUrl uri = job->resourceUri();
+    QVERIFY(!uri.isEmpty());
+
+    QVERIFY(m_model->containsAnyStatement(uri, RDF::type(), QUrl("class:/A")));
+    QVERIFY(m_model->containsAnyStatement(uri, RDF::type(), QUrl("class:/B")));
+    QVERIFY(m_model->containsAnyStatement(uri, NAO::prefLabel(), LiteralValue(QLatin1String("label"))));
+    QVERIFY(m_model->containsAnyStatement(uri, NAO::description(), LiteralValue(QLatin1String("desc"))));
+}
+
+void AsyncClientApiTest::testRemoveProperty()
+{
+    Soprano::NRLModel nrlModel(m_model);
+    const QUrl g1 = nrlModel.createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    KJob* job = Nepomuk::removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/string"), QVariantList() << QLatin1String("hello world"));
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    // test that the data has been removed
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world"))));
+}
+
+void AsyncClientApiTest::testRemoveResources()
+{
+    Soprano::NRLModel nrlModel(m_model);
+    const QUrl g1 = nrlModel.createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("res:/A"), RDF::type(), NAO::Tag(), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+
+    KJob* job = Nepomuk::removeResources(QList<QUrl>() << QUrl("res:/A"), NoRemovalFlags);
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    // verify that the resource is gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+}
+
+void AsyncClientApiTest::testRemoveDataByApplication()
+{
+    Soprano::NRLModel nrlModel(m_model);
+
+    // create our apps (we need to use the component name for the first one as that will be reused in the call below)
+    QUrl appG = nrlModel.createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(KGlobal::mainComponent().componentName()), appG);
+    appG = nrlModel.createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = nrlModel.createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    QUrl mg2;
+    const QUrl g2 = nrlModel.createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+
+    // delete the resource
+    KJob* job = Nepomuk::removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), NoRemovalFlags);
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    // verify that graph1 is gone completely
+    QVERIFY(!m_model->containsAnyStatement(Node(), Node(), Node(), g1));
+
+    // only two statements left: the one in the second graph and the last modification date
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), Node(), Node()).allStatements().count(), 2);
+    QVERIFY(m_model->containsStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NAO::lastModified(), Node()));
+
+    // four graphs: g2, the 2 app graphs, and the mtime graph
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NRL::InstanceBase()).allStatements().count(), 4);
+}
+
+void AsyncClientApiTest::testStoreResources()
+{
+    // store a resource just to check if the method is called properly
+    // and all types are property handled
+    SimpleResource res;
+    res.setUri(QUrl("_:A"));
+    res.addProperty(RDF::type(), NAO::Tag());
+    res.addProperty(QUrl("prop:/string"), QLatin1String("Foobar"));
+    res.addProperty(QUrl("prop:/int"), 42);
+    res.addProperty(QUrl("prop:/date"), QDate::currentDate());
+    res.addProperty(QUrl("prop:/time"), QTime::currentTime());
+    res.addProperty(QUrl("prop:/dateTime"), QDateTime::currentDateTime());
+    res.addProperty(QUrl("prop:/res"), QUrl("res:/A"));
+
+    KJob* job = Nepomuk::storeResources(SimpleResourceGraph() << res);
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    // check if the resource exists
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), RDF::type(), NAO::Tag()));
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), QUrl("prop:/string"), Soprano::LiteralValue(QLatin1String("Foobar"))));
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), QUrl("prop:/int"), Soprano::LiteralValue(42)));
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), QUrl("prop:/date"), Soprano::LiteralValue(res.property(QUrl("prop:/date")).first().toDate())));
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), QUrl("prop:/time"), Soprano::LiteralValue(res.property(QUrl("prop:/time")).first().toTime())));
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), QUrl("prop:/dateTime"), Soprano::LiteralValue(res.property(QUrl("prop:/dateTime")).first().toDateTime())));
+}
+
+void AsyncClientApiTest::testMergeResources()
+{
+    // create some resources
+    Soprano::NRLModel nrlModel(m_model);
+    const QUrl g1 = nrlModel.createGraph(NRL::InstanceBase());
+
+    // the resource in which we want to merge
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int_c1"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // the resource that is going to be merged
+    // one duplicate property and one that differs, one backlink to ignore,
+    // one property with cardinality 1 to ignore
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int_c1"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+
+    KJob* job = Nepomuk::mergeResources(QUrl("res:/A"), QUrl("res:/B"));
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    // make sure B is gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(Node(), Node(), QUrl("res:/B")));
+
+    // make sure A has all the required properties
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42)));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int_c1"), LiteralValue(42)));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello"))));
+
+    // make sure A has no superfluous properties
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int_c1"), LiteralValue(12)));
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/int"), Node()).allElements().count(), 1);
+}
+
+void AsyncClientApiTest::testDescribeResources()
+{
+    // create some resources
+    Soprano::NRLModel nrlModel(m_model);
+    const QUrl g1 = nrlModel.createGraph(NRL::InstanceBase());
+
+    m_model->addStatement(QUrl("res:/A"), RDF::type(), NAO::Tag(), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/B"), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/C"), NAO::hasSubResource(), QUrl("res:/D"), g1);
+
+    m_model->addStatement(QUrl("res:/D"), QUrl("prop:/string"), LiteralValue(QLatin1String("Hello")), g1);
+
+
+    // we only use one of the test cases from the dms test: get two resources with subresoruces
+    DescribeResourcesJob* job = Nepomuk::describeResources(QList<QUrl>() << QUrl("res:/A") << QUrl("res:/C"), true);
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+    QList<SimpleResource> g = job->resources().toList();
+
+    // only one resource in the result
+    QCOMPARE(g.count(), 4);
+
+    // the results are res:/A, res:/B, res:/C and res:/D
+    QList<SimpleResource>::const_iterator it = g.constBegin();
+    SimpleResource r1 = *it;
+    ++it;
+    SimpleResource r2 = *it;
+    ++it;
+    SimpleResource r3 = *it;
+    ++it;
+    SimpleResource r4 = *it;
+    QVERIFY(r1.uri() == QUrl("res:/A") || r2.uri() == QUrl("res:/A") || r3.uri() == QUrl("res:/A") || r4.uri() == QUrl("res:/A"));
+    QVERIFY(r1.uri() == QUrl("res:/B") || r2.uri() == QUrl("res:/B") || r3.uri() == QUrl("res:/B") || r4.uri() == QUrl("res:/B"));
+    QVERIFY(r1.uri() == QUrl("res:/C") || r2.uri() == QUrl("res:/C") || r3.uri() == QUrl("res:/C") || r4.uri() == QUrl("res:/C"));
+    QVERIFY(r1.uri() == QUrl("res:/D") || r2.uri() == QUrl("res:/D") || r3.uri() == QUrl("res:/D") || r4.uri() == QUrl("res:/D"));
+}
+
+void AsyncClientApiTest::testImportResources()
+{
+    // create the test data
+    QTemporaryFile fileA;
+    fileA.open();
+
+    Soprano::Graph graph;
+    graph.addStatement(Node(QString::fromLatin1("res1")), QUrl("prop:/int"), LiteralValue(42));
+    graph.addStatement(Node(QString::fromLatin1("res1")), RDF::type(), QUrl("class:/typeA"));
+    graph.addStatement(Node(QString::fromLatin1("res1")), QUrl("prop:/res"), Node(QString::fromLatin1("res2")));
+    graph.addStatement(Node(QString::fromLatin1("res2")), RDF::type(), QUrl("class:/typeB"));
+    graph.addStatement(QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/int"), LiteralValue(12));
+    graph.addStatement(QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")));
+
+    // write the test file
+    QTemporaryFile tmp;
+    tmp.open();
+    QTextStream str(&tmp);
+    Q_FOREACH(const Statement& s, graph.toList()) {
+        str << s.subject().toN3() << " " << s.predicate().toN3() << " " << s.object().toN3() << " ." << endl;
+    }
+    tmp.close();
+
+
+    // import the file
+    KJob* job = Nepomuk::importResources(QUrl::fromLocalFile(tmp.fileName()), Soprano::SerializationNTriples);
+    QTest::kWaitForSignal(job, SIGNAL(result(KJob*)), 5000);
+    QVERIFY(!job->error());
+
+
+    // make sure the data has been imported properly
+    QVERIFY(m_model->containsAnyStatement(Node(), QUrl("prop:/int"), LiteralValue(42)));
+    const QUrl res1Uri = m_model->listStatements(Node(), QUrl("prop:/int"), LiteralValue(42)).allStatements().first().subject().uri();
+    QVERIFY(m_model->containsAnyStatement(res1Uri, RDF::type(), QUrl("class:/typeA")));
+    QVERIFY(m_model->containsAnyStatement(res1Uri, QUrl("prop:/res"), Node()));
+    const QUrl res2Uri = m_model->listStatements(res1Uri, QUrl("prop:/res"), Node()).allStatements().first().object().uri();
+    QVERIFY(m_model->containsAnyStatement(res2Uri, RDF::type(), QUrl("class:/typeB")));
+    QVERIFY(m_model->containsAnyStatement(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())));
+    const QUrl res3Uri = m_model->listStatements(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())).allStatements().first().subject().uri();
+    QVERIFY(m_model->containsAnyStatement(res3Uri, QUrl("prop:/int"), LiteralValue(12)));
+    QVERIFY(m_model->containsAnyStatement(res3Uri, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+
+    // make sure the metadata is there
+    QVERIFY(m_model->containsAnyStatement(res1Uri, NAO::lastModified(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res1Uri, NAO::created(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res2Uri, NAO::lastModified(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res2Uri, NAO::created(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res3Uri, NAO::lastModified(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res3Uri, NAO::created(), Node()));
+}
+
+QTEST_KDEMAIN_CORE(AsyncClientApiTest)
+
+#include "asyncclientapitest.moc"
diff --git a/nepomuk/services/storage/test/asyncclientapitest.h b/nepomuk/services/storage/test/asyncclientapitest.h
new file mode 100644
index 0000000..3cb6bec
--- /dev/null
+++ b/nepomuk/services/storage/test/asyncclientapitest.h
@@ -0,0 +1,59 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ASYNCCLIENTAPITEST_H
+#define ASYNCCLIENTAPITEST_H
+
+#include <QObject>
+
+class QProcess;
+namespace Soprano {
+class Model;
+}
+class AsyncClientApiTest : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+
+    void testAddProperty();
+    void testSetProperty();
+    void testCreateResource();
+    void testRemoveProperty();
+    void testRemoveProperties();
+    void testRemoveResources();
+    void testRemoveDataByApplication();
+    void testStoreResources();
+    void testMergeResources();
+    void testDescribeResources();
+    void testImportResources();
+
+private:
+    void resetModel();
+
+    QProcess* m_fakeDms;
+    Soprano::Model* m_model;
+};
+
+#endif
diff --git a/nepomuk/services/storage/test/classandpropertytreetest.cpp b/nepomuk/services/storage/test/classandpropertytreetest.cpp
new file mode 100644
index 0000000..6d6ee75
--- /dev/null
+++ b/nepomuk/services/storage/test/classandpropertytreetest.cpp
@@ -0,0 +1,290 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "classandpropertytreetest.h"
+#include "../classandpropertytree.h"
+
+#include <QtTest>
+#include "qtest_kde.h"
+#include "qtest_dms.h"
+
+#include <Soprano/Soprano>
+
+#include <ktempdir.h>
+#include <kdebug.h>
+
+using namespace Soprano;
+using namespace Soprano::Vocabulary;
+
+Q_DECLARE_METATYPE(Soprano::Node)
+
+void ClassAndPropertyTreeTest::initTestCase()
+{
+    // we need to use a Virtuoso model as tmp model since redland misses important SPARQL features
+    // that are used by libnepomuk below
+    const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByName( "virtuosobackend" );
+    QVERIFY( backend );
+    m_storageDir = new KTempDir();
+    m_model = backend->createModel( Soprano::BackendSettings() << Soprano::BackendSetting(Soprano::BackendOptionStorageDir, m_storageDir->name()) );
+    QVERIFY( m_model );
+
+    m_typeTree = new Nepomuk::ClassAndPropertyTree( this );
+}
+
+void ClassAndPropertyTreeTest::cleanupTestCase()
+{
+    delete m_model;
+    delete m_typeTree;
+    delete m_storageDir;
+}
+
+
+void ClassAndPropertyTreeTest::init()
+{
+    m_model->removeAllStatements();
+
+    // we create one fake ontology
+    //
+    // situations we need to test:
+    // * class that is marked visible should stay visible
+    // * class that is marked invisible should stay invisible
+    // * non-marked subclass of visible should be visible, too
+    // * non-marked subclass of invisible should be invisible, too
+    // * marked subclass should keep its own visiblity and not inherit from parent
+    // * whole branch should inherit from parent
+    // * if one parent is visible the class is visible, too, even if N other parents are not
+    // * if all parents are invisible, the class is invisible, even if higher up in the branch a class is visible
+    // * properly handle loops (as in: do not run into an endless loop)
+    //
+    // A
+    // |- B - invisible
+    //    |- C
+    //       |- D - visible
+    //          |- E
+    //             |- F
+    //    |- G
+    //
+    // AA - invisible
+    // | - F
+    // | - G
+    //
+    // X
+    // |- Y - invisible
+    //    |- Z
+    //       |- X
+
+    QUrl graph("graph:/onto");
+    m_model->addStatement( graph, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::Ontology(), graph );
+
+    m_model->addStatement( QUrl("onto:/A"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/B"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/C"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/D"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/E"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/F"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/G"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/AA"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/X"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/Y"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+    m_model->addStatement( QUrl("onto:/Z"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
+
+    m_model->addStatement( QUrl("onto:/B"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/A"), graph );
+    m_model->addStatement( QUrl("onto:/C"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/B"), graph );
+    m_model->addStatement( QUrl("onto:/D"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/C"), graph );
+    m_model->addStatement( QUrl("onto:/E"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/D"), graph );
+    m_model->addStatement( QUrl("onto:/F"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/E"), graph );
+    m_model->addStatement( QUrl("onto:/G"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/B"), graph );
+    m_model->addStatement( QUrl("onto:/F"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/AA"), graph );
+    m_model->addStatement( QUrl("onto:/G"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/AA"), graph );
+    m_model->addStatement( QUrl("onto:/Y"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/X"), graph );
+    m_model->addStatement( QUrl("onto:/Z"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/Y"), graph );
+    m_model->addStatement( QUrl("onto:/X"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/Z"), graph );
+
+    m_model->addStatement( QUrl("onto:/B"), Soprano::Vocabulary::NAO::userVisible(), LiteralValue(false), graph );
+    m_model->addStatement( QUrl("onto:/D"), Soprano::Vocabulary::NAO::userVisible(), LiteralValue(true), graph );
+    m_model->addStatement( QUrl("onto:/AA"), Soprano::Vocabulary::NAO::userVisible(), LiteralValue(false), graph );
+    m_model->addStatement( QUrl("onto:/Y"), Soprano::Vocabulary::NAO::userVisible(), LiteralValue(false), graph );
+
+    // a few properties for node conversion testing and range checking
+    m_model->addStatement( QUrl("prop:/A"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/B"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/B1"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/B2"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/C"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/D"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/E"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDF::Property(), graph );
+
+    m_model->addStatement( QUrl("prop:/A"), Soprano::Vocabulary::RDFS::range(), Soprano::Vocabulary::XMLSchema::string(), graph );
+    m_model->addStatement( QUrl("prop:/B"), Soprano::Vocabulary::RDFS::range(), Soprano::Vocabulary::XMLSchema::xsdInt(), graph );
+    m_model->addStatement( QUrl("prop:/B1"), Soprano::Vocabulary::RDFS::range(), Soprano::Vocabulary::XMLSchema::integer(), graph );
+    m_model->addStatement( QUrl("prop:/B2"), Soprano::Vocabulary::RDFS::range(), Soprano::Vocabulary::XMLSchema::unsignedInt(), graph );
+    m_model->addStatement( QUrl("prop:/C"), Soprano::Vocabulary::RDFS::range(), Soprano::Vocabulary::XMLSchema::xsdDouble(), graph );
+    m_model->addStatement( QUrl("prop:/D"), Soprano::Vocabulary::RDFS::range(), Soprano::Vocabulary::XMLSchema::dateTime(), graph );
+    m_model->addStatement( QUrl("prop:/E"), Soprano::Vocabulary::RDFS::range(), QUrl("onto:/A"), graph );
+
+    m_model->addStatement( QUrl("prop:/C"), Soprano::Vocabulary::NRL::maxCardinality(), LiteralValue(1), graph );
+    m_model->addStatement( QUrl("prop:/D"), Soprano::Vocabulary::NRL::cardinality(), LiteralValue(1), graph );
+
+    m_typeTree->rebuildTree(m_model);
+}
+
+void ClassAndPropertyTreeTest::testVisibility()
+{
+    QVERIFY(m_typeTree->isUserVisible(QUrl("onto:/A")));
+    QVERIFY(!m_typeTree->isUserVisible(QUrl("onto:/B")));
+    QVERIFY(!m_typeTree->isUserVisible(QUrl("onto:/C")));
+    QVERIFY(m_typeTree->isUserVisible(QUrl("onto:/D")));
+    QVERIFY(m_typeTree->isUserVisible(QUrl("onto:/E")));
+    QVERIFY(m_typeTree->isUserVisible(QUrl("onto:/F")));
+    QVERIFY(!m_typeTree->isUserVisible(QUrl("onto:/G")));
+    QVERIFY(!m_typeTree->isUserVisible(QUrl("onto:/AA")));
+    QVERIFY(!m_typeTree->isUserVisible(QUrl("onto:/X"))); // because only top-level classes inherit from rdfs:Resource
+    QVERIFY(!m_typeTree->isUserVisible(QUrl("onto:/Y")));
+    QVERIFY(!m_typeTree->isUserVisible(QUrl("onto:/Z")));
+}
+
+void ClassAndPropertyTreeTest::testParents()
+{
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/A")).count(), 1);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/A"), Soprano::Vocabulary::RDFS::Resource()));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/B")).count(), 2);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/B"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/B"), QUrl("onto:/A")));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/C")).count(), 3);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/C"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/C"), QUrl("onto:/A")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/C"), QUrl("onto:/B")));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/D")).count(), 4);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/D"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/D"), QUrl("onto:/A")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/D"), QUrl("onto:/B")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/D"), QUrl("onto:/C")));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/E")).count(), 5);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/E"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/E"), QUrl("onto:/A")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/E"), QUrl("onto:/B")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/E"), QUrl("onto:/C")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/E"), QUrl("onto:/D")));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/F")).count(), 7);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/F"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/F"), QUrl("onto:/A")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/F"), QUrl("onto:/B")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/F"), QUrl("onto:/C")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/F"), QUrl("onto:/D")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/F"), QUrl("onto:/E")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/F"), QUrl("onto:/AA")));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/G")).count(), 4);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/G"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/G"), QUrl("onto:/AA")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/G"), QUrl("onto:/A")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/G"), QUrl("onto:/B")));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/X")).count(), 3);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/X"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/X"), QUrl("onto:/Y")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/X"), QUrl("onto:/Z")));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/Y")).count(), 3);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/Y"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/Y"), QUrl("onto:/X")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/Y"), QUrl("onto:/Z")));
+
+    QCOMPARE(m_typeTree->allParents(QUrl("onto:/Z")).count(), 3);
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/Z"), Soprano::Vocabulary::RDFS::Resource()));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/Z"), QUrl("onto:/X")));
+    QVERIFY(m_typeTree->isChildOf(QUrl("onto:/Z"), QUrl("onto:/Y")));
+}
+
+void ClassAndPropertyTreeTest::testVariantToNode_data()
+{
+    QTest::addColumn<QVariant>( "value" );
+    QTest::addColumn<QUrl>( "property" );
+    QTest::addColumn<Soprano::Node>( "node" );
+
+    // simple literal values
+    QTest::newRow("string-simple") << QVariant("foobar") << QUrl("prop:/A") << Soprano::Node(LiteralValue(QLatin1String("foobar")));
+    QTest::newRow("int-simple") << QVariant(42) << QUrl("prop:/B") << Soprano::Node(LiteralValue(42));
+    QTest::newRow("double-simple") << QVariant(42.1) << QUrl("prop:/C") << Soprano::Node(LiteralValue(42.1));
+    const QDateTime now = QDateTime::currentDateTime();
+    QTest::newRow("datatime-simple") << QVariant(now) << QUrl("prop:/D") << Soprano::Node(LiteralValue(now));
+
+    // literal values that can be converted to strings
+    QTest::newRow("string-int") << QVariant(42) << QUrl("prop:/A") << Soprano::Node(LiteralValue(QLatin1String("42")));
+    QTest::newRow("string-double") << QVariant(42.2) << QUrl("prop:/A") << Soprano::Node(LiteralValue(QLatin1String("42.2")));
+    QTest::newRow("string-datetime") << QVariant(now) << QUrl("prop:/A") << Soprano::Node(LiteralValue(QVariant(now).toString()));
+
+    // literal values that can be converted from strings
+    QTest::newRow("int-string") << QVariant("42") << QUrl("prop:/B") << Soprano::Node(LiteralValue(42));
+    QTest::newRow("double-string") << QVariant("42.2") << QUrl("prop:/C") << Soprano::Node(LiteralValue(42.2));
+    QTest::newRow("datetime-string") << QVariant(LiteralValue(now).toString()) << QUrl("prop:/D") << Soprano::Node(LiteralValue(now));
+
+    // different types of int
+    QTest::newRow("int-unsigned") << QVariant(42) << QUrl("prop:/B2") << Soprano::Node(LiteralValue(uint(42)));
+    QTest::newRow("int-integer") << QVariant(42) << QUrl("prop:/B1") << Soprano::Node(LiteralValue::fromString(QLatin1String("42"), XMLSchema::integer()));
+
+    // literal values that cannot be converted
+    QTest::newRow("int-invalid") << QVariant("43g") << QUrl("prop:/B") << Soprano::Node();
+    QTest::newRow("double-invalid") << QVariant("43g") << QUrl("prop:/C") << Soprano::Node();
+
+    // resource URI
+    QTest::newRow("res-uri") << QVariant(QUrl("res:/A")) << QUrl("prop:/E") << Soprano::Node(QUrl("res:/A"));
+    QTest::newRow("res-string") << QVariant(QLatin1String("res:/A")) << QUrl("prop:/E") << Soprano::Node(QUrl("res:/A"));
+
+    // local file
+    QTest::newRow("file-path") << QVariant(QLatin1String("/tmp")) << QUrl("prop:/E") << Soprano::Node(QUrl("file:///tmp"));
+}
+
+void ClassAndPropertyTreeTest::testVariantToNode()
+{
+    QFETCH(QVariant, value);
+    QFETCH(QUrl, property);
+    QFETCH(Soprano::Node, node);
+
+    QCOMPARE(m_typeTree->variantToNode(value, property), node);
+}
+
+void ClassAndPropertyTreeTest::testProperties()
+{
+    QCOMPARE(m_typeTree->maxCardinality(QUrl("prop:/C")), 1);
+    QCOMPARE(m_typeTree->maxCardinality(QUrl("prop:/D")), 1);
+}
+
+void ClassAndPropertyTreeTest::testVisibleType()
+{
+    const QList<QUrl> types = m_typeTree->visibleTypes();
+    kDebug() << types;
+    QCOMPARE(types.count(), 5);
+    QVERIFY(types.contains(RDFS::Resource()));
+    QVERIFY(types.contains(QUrl("onto:/A")));
+    QVERIFY(types.contains(QUrl("onto:/D")));
+    QVERIFY(types.contains(QUrl("onto:/E")));
+    QVERIFY(types.contains(QUrl("onto:/F")));
+}
+
+QTEST_KDEMAIN_CORE(ClassAndPropertyTreeTest)
+
+#include "classandpropertytreetest.moc"
diff --git a/nepomuk/services/storage/test/classandpropertytreetest.h b/nepomuk/services/storage/test/classandpropertytreetest.h
new file mode 100644
index 0000000..29d961b
--- /dev/null
+++ b/nepomuk/services/storage/test/classandpropertytreetest.h
@@ -0,0 +1,56 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CLASSANDPROPERTYTREETEST_H
+#define CLASSANDPROPERTYTREETEST_H
+
+#include <QObject>
+
+class KTempDir;
+namespace Soprano {
+class Model;
+}
+namespace Nepomuk {
+class ClassAndPropertyTree;
+}
+
+class ClassAndPropertyTreeTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+    void testVisibility();
+    void testParents();
+    void testVariantToNode_data();
+    void testVariantToNode();
+    void testProperties();
+    void testVisibleType();
+
+private:
+    KTempDir* m_storageDir;
+    Soprano::Model* m_model;
+    Nepomuk::ClassAndPropertyTree* m_typeTree;
+};
+
+#endif
diff --git a/nepomuk/services/storage/test/crappyinferencer2test.cpp b/nepomuk/services/storage/test/crappyinferencer2test.cpp
index 0350058..f70b767 100644
--- a/nepomuk/services/storage/test/crappyinferencer2test.cpp
+++ b/nepomuk/services/storage/test/crappyinferencer2test.cpp
@@ -21,6 +21,7 @@
 
 #include "crappyinferencer2test.h"
 #include "../crappyinferencer2.h"
+#include "../classandpropertytree.h"
 
 #include <QtTest>
 #include "qtest_kde.h"
@@ -45,7 +46,8 @@ void CrappyInferencer2Test::initTestCase()
     m_baseModel = backend->createModel( Soprano::BackendSettings() << Soprano::BackendSetting(Soprano::BackendOptionStorageDir, m_storageDir->name()) );
     QVERIFY( m_baseModel );
 
-    m_model = new CrappyInferencer2( m_baseModel );
+    m_typeTree = new Nepomuk::ClassAndPropertyTree( this );
+    m_model = new CrappyInferencer2( m_typeTree, m_baseModel );
 }
 
 void CrappyInferencer2Test::cleanupTestCase()
@@ -101,6 +103,7 @@ void CrappyInferencer2Test::init()
     m_baseModel->addStatement( QUrl("onto:/L2"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
     m_baseModel->addStatement( QUrl("onto:/L2"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/L1"), graph );
 
+    m_typeTree->rebuildTree(m_model);
     m_model->updateInferenceIndex();
 }
 
diff --git a/nepomuk/services/storage/test/crappyinferencer2test.h b/nepomuk/services/storage/test/crappyinferencer2test.h
index cafe073..1ad41dd 100644
--- a/nepomuk/services/storage/test/crappyinferencer2test.h
+++ b/nepomuk/services/storage/test/crappyinferencer2test.h
@@ -29,6 +29,9 @@ class CrappyInferencer2;
 namespace Soprano {
 class Model;
 }
+namespace Nepomuk {
+class ClassAndPropertyTree;
+}
 
 class CrappyInferencer2Test : public QObject
 {
@@ -51,6 +54,7 @@ private Q_SLOTS:
 private:
     KTempDir* m_storageDir;
     Soprano::Model* m_baseModel;
+    Nepomuk::ClassAndPropertyTree* m_typeTree;
     CrappyInferencer2* m_model;
 };
 
diff --git a/nepomuk/services/storage/test/datamanagementadaptortest.cpp b/nepomuk/services/storage/test/datamanagementadaptortest.cpp
new file mode 100644
index 0000000..78d47d4
--- /dev/null
+++ b/nepomuk/services/storage/test/datamanagementadaptortest.cpp
@@ -0,0 +1,131 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "datamanagementadaptortest.h"
+#include "../datamanagementmodel.h"
+#include "../datamanagementadaptor.h"
+#include "../classandpropertytree.h"
+#include "simpleresource.h"
+
+#include <QtTest>
+#include "qtest_kde.h"
+
+#include <Soprano/Soprano>
+#include <Soprano/Graph>
+#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
+#include <Soprano/NRLModel>
+
+#include <ktempdir.h>
+#include <KDebug>
+
+#include <Nepomuk/Vocabulary/NFO>
+#include <Nepomuk/Vocabulary/NMM>
+#include <Nepomuk/Vocabulary/NCO>
+#include <Nepomuk/Vocabulary/NIE>
+
+using namespace Soprano;
+using namespace Soprano::Vocabulary;
+using namespace Nepomuk;
+using namespace Nepomuk::Vocabulary;
+
+
+void DataManagementAdaptorTest::resetModel()
+{
+    // remove all the junk from previous tests
+    m_model->removeAllStatements();
+
+    // add some classes and properties
+    QUrl graph1("graph:/onto1/");
+    m_model->addStatement( graph1, RDF::type(), NRL::Ontology(), graph1 );
+    m_model->addStatement( graph1, NAO::hasDefaultNamespace(), QUrl("graph:/onto1/"), graph1 );
+    m_model->addStatement( graph1, NAO::hasDefaultNamespaceAbbreviation(), LiteralValue(QLatin1String("itda")), graph1 );
+
+    QUrl graph2("graph:/onto2#");
+    m_model->addStatement( graph2, RDF::type(), NRL::Ontology(), graph2 );
+    m_model->addStatement( graph2, NAO::hasDefaultNamespace(), QUrl("graph:/onto2#"), graph1 );
+    m_model->addStatement( graph2, NAO::hasDefaultNamespaceAbbreviation(), LiteralValue(QLatin1String("wbzo")), graph1 );
+
+
+    m_model->addStatement( QUrl("graph:/onto1/P1"), RDF::type(), RDF::Property(), graph1 );
+    m_model->addStatement( QUrl("graph:/onto1/P2"), RDF::type(), RDF::Property(), graph1 );
+    m_model->addStatement( QUrl("graph:/onto1/T1"), RDFS::Class(), RDF::Property(), graph1 );
+
+    m_model->addStatement( QUrl("graph:/onto2#P1"), RDF::type(), RDF::Property(), graph2 );
+    m_model->addStatement( QUrl("graph:/onto2#P2"), RDF::type(), RDF::Property(), graph2 );
+    m_model->addStatement( QUrl("graph:/onto2#T1"), RDFS::Class(), RDF::Property(), graph2 );
+
+
+    m_classAndPropertyTree->rebuildTree(m_dmModel);
+    m_nrlModel->setEnableQueryPrefixExpansion(false);
+    m_nrlModel->setEnableQueryPrefixExpansion(true);
+    QHash<QString, QString> prefixes;
+    const QHash<QString, QUrl> namespaces = m_nrlModel->queryPrefixes();
+    for(QHash<QString, QUrl>::const_iterator it = namespaces.constBegin();
+        it != namespaces.constEnd(); ++it) {
+        prefixes.insert(it.key(), QString::fromAscii(it.value().toEncoded()));
+    }
+    m_dmAdaptor->setPrefixes(prefixes);
+}
+
+void DataManagementAdaptorTest::initTestCase()
+{
+    const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByName( "virtuosobackend" );
+    QVERIFY( backend );
+    m_storageDir = new KTempDir();
+    m_model = backend->createModel( Soprano::BackendSettings() << Soprano::BackendSetting(Soprano::BackendOptionStorageDir, m_storageDir->name()) );
+    QVERIFY( m_model );
+
+    // DataManagementModel relies on the ussage of a NRLModel in the storage service
+    m_nrlModel = new Soprano::NRLModel(m_model);
+
+    m_classAndPropertyTree = new Nepomuk::ClassAndPropertyTree(this);
+
+    m_dmModel = new Nepomuk::DataManagementModel(m_classAndPropertyTree, m_nrlModel);
+
+    m_dmAdaptor = new Nepomuk::DataManagementAdaptor(m_dmModel);
+}
+
+void DataManagementAdaptorTest::cleanupTestCase()
+{
+    delete m_dmAdaptor;
+    delete m_dmModel;
+    delete m_nrlModel;
+    delete m_model;
+    delete m_storageDir;
+}
+
+void DataManagementAdaptorTest::init()
+{
+    resetModel();
+}
+
+void DataManagementAdaptorTest::testNamespaceExpansion()
+{
+    QCOMPARE(m_dmAdaptor->decodeUri(QLatin1String("itda:P1"), true), QUrl("graph:/onto1/P1"));
+    QCOMPARE(m_dmAdaptor->decodeUri(QLatin1String("itda:T1"), true), QUrl("graph:/onto1/T1"));
+    QCOMPARE(m_dmAdaptor->decodeUri(QLatin1String("wbzo:P1"), true), QUrl("graph:/onto2#P1"));
+    QCOMPARE(m_dmAdaptor->decodeUri(QLatin1String("wbzo:T1"), true), QUrl("graph:/onto2#T1"));
+}
+
+
+QTEST_KDEMAIN_CORE(DataManagementAdaptorTest)
+
+#include "datamanagementadaptortest.moc"
diff --git a/nepomuk/services/storage/test/datamanagementadaptortest.h b/nepomuk/services/storage/test/datamanagementadaptortest.h
new file mode 100644
index 0000000..a7c4652
--- /dev/null
+++ b/nepomuk/services/storage/test/datamanagementadaptortest.h
@@ -0,0 +1,60 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DATAMANAGEMENTADAPTORTEST_H
+#define DATAMANAGEMENTADAPTORTEST_H
+
+#include <QObject>
+
+namespace Soprano {
+class Model;
+class NRLModel;
+}
+namespace Nepomuk {
+class DataManagementModel;
+class DataManagementAdaptor;
+class ClassAndPropertyTree;
+}
+class KTempDir;
+
+class DataManagementAdaptorTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+
+    void testNamespaceExpansion();
+
+private:
+    void resetModel();
+
+    KTempDir* m_storageDir;
+    Soprano::Model* m_model;
+    Soprano::NRLModel* m_nrlModel;
+    Nepomuk::ClassAndPropertyTree* m_classAndPropertyTree;
+    Nepomuk::DataManagementModel* m_dmModel;
+    Nepomuk::DataManagementAdaptor* m_dmAdaptor;
+};
+
+#endif
diff --git a/nepomuk/services/storage/test/datamanagementmodeltest.cpp b/nepomuk/services/storage/test/datamanagementmodeltest.cpp
new file mode 100644
index 0000000..89914e6
--- /dev/null
+++ b/nepomuk/services/storage/test/datamanagementmodeltest.cpp
@@ -0,0 +1,5032 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "datamanagementmodeltest.h"
+#include "../datamanagementmodel.h"
+#include "../classandpropertytree.h"
+#include "simpleresource.h"
+#include "simpleresourcegraph.h"
+
+#include <QtTest>
+#include "qtest_kde.h"
+#include "qtest_dms.h"
+
+#include <Soprano/Soprano>
+#include <Soprano/Graph>
+#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
+#include <Soprano/NRLModel>
+
+#include <KTemporaryFile>
+#include <KTempDir>
+#include <KProtocolInfo>
+#include <KDebug>
+
+#include <Nepomuk/Vocabulary/NFO>
+#include <Nepomuk/Vocabulary/NMM>
+#include <Nepomuk/Vocabulary/NCO>
+#include <Nepomuk/Vocabulary/NIE>
+#include <Nepomuk/ResourceManager>
+
+using namespace Soprano;
+using namespace Soprano::Vocabulary;
+using namespace Nepomuk;
+using namespace Nepomuk::Vocabulary;
+
+
+// TODO: test nao:created and nao:lastModified, these should always be correct for existing resources. This is especially important in the removeDataByApplication methods.
+
+void DataManagementModelTest::resetModel()
+{
+    // remove all the junk from previous tests
+    m_model->removeAllStatements();
+
+    // add some classes and properties
+    QUrl graph("graph:/onto");
+    Nepomuk::insertOntologies( m_model, graph );
+
+    // rebuild the internals of the data management model
+    m_classAndPropertyTree->rebuildTree(m_dmModel);
+}
+
+
+void DataManagementModelTest::initTestCase()
+{
+    const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByName( "virtuosobackend" );
+    QVERIFY( backend );
+    m_storageDir = new KTempDir();
+    m_model = backend->createModel( Soprano::BackendSettings() << Soprano::BackendSetting(Soprano::BackendOptionStorageDir, m_storageDir->name()) );
+    QVERIFY( m_model );
+
+    // DataManagementModel relies on the ussage of a NRLModel in the storage service
+    m_nrlModel = new Soprano::NRLModel(m_model);
+    m_classAndPropertyTree = new Nepomuk::ClassAndPropertyTree(this);
+    m_dmModel = new Nepomuk::DataManagementModel(m_classAndPropertyTree, m_nrlModel);
+}
+
+void DataManagementModelTest::cleanupTestCase()
+{
+    delete m_dmModel;
+    delete m_nrlModel;
+    delete m_model;
+    delete m_storageDir;
+    delete m_classAndPropertyTree;
+}
+
+void DataManagementModelTest::init()
+{
+    resetModel();
+}
+
+
+void DataManagementModelTest::testAddProperty()
+{
+    // we start by simply adding a property
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/string"), QVariantList() << QVariant(QLatin1String("foobar")), QLatin1String("Testapp"));
+
+    QVERIFY(!m_dmModel->lastError());
+
+    // check that the actual data is there
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+
+    // check that the app resource has been created with its corresponding graphs
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { ?r a %1 . ?r %2 %3 . } . "
+                                                      "graph ?mg { ?g a %4 . ?mg a %5 . ?mg %6 ?g . } . }")
+                                  .arg(Soprano::Node::resourceToN3(NAO::Agent()),
+                                       Soprano::Node::resourceToN3(NAO::identifier()),
+                                       Soprano::Node::literalToN3(QLatin1String("Testapp")),
+                                       Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                                       Soprano::Node::resourceToN3(NRL::GraphMetadata()),
+                                       Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor())),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    // check that we have an InstanceBase with a GraphMetadata graph
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { <nepomuk:/res/A> <prop:/string> %1 . } . "
+                                                      "graph ?mg { ?g a %2 . ?mg a %3 . ?mg %4 ?g . } . "
+                                                      "}")
+                                  .arg(Soprano::Node::literalToN3(QLatin1String("foobar")),
+                                       Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                                       Soprano::Node::resourceToN3(NRL::GraphMetadata()),
+                                       Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor())),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    // check the number of graphs (two for the app, two for the actual data, and one for the ontology)
+    QCOMPARE(m_model->listContexts().allElements().count(), 5);
+
+
+    //
+    // add another property value on top of the existing one
+    //
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/string"), QVariantList() << QVariant(QLatin1String("hello world")), QLatin1String("Testapp"));
+
+    // verify the values
+    QCOMPARE(m_model->listStatements(QUrl("nepomuk:/res/A"), QUrl("prop:/string"), Node()).allStatements().count(), 2);
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world"))));
+
+    // check that we only have one agent instance
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NAO::Agent()).allStatements().count(), 1);
+
+    //
+    // rewrite the same property with the same app
+    //
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/string"), QVariantList() << QVariant(QLatin1String("hello world")), QLatin1String("Testapp"));
+
+    // nothing should have changed
+    QCOMPARE(existingStatements, Soprano::Graph(m_model->listStatements().allStatements()));
+
+
+    //
+    // rewrite the same property with another app
+    //
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/string"), QVariantList() << QVariant(QLatin1String("hello world")), QLatin1String("Otherapp"));
+
+    // there should only be the new app, nothing else
+    // thus, all previous statements need to be there
+    foreach(const Statement& s, existingStatements.toList()) {
+        QVERIFY(m_model->containsStatement(s));
+    }
+
+
+    // plus the new app
+    existingStatements.addStatements(
+                m_model->executeQuery(QString::fromLatin1("select ?g ?s ?p ?o where { graph ?g { ?s ?p ?o . } . filter(bif:exists((select (1) where { graph ?g { ?a a %1 . ?a %2 %3 . } . })))}")
+                                      .arg(Soprano::Node::resourceToN3(NAO::Agent()),
+                                           Soprano::Node::resourceToN3(NAO::identifier()),
+                                           Soprano::Node::literalToN3(QLatin1String("Otherapp"))),
+                                      Soprano::Query::QueryLanguageSparql)
+                .iterateStatementsFromBindings(QLatin1String("s"), QLatin1String("p"), QLatin1String("o"), QLatin1String("g"))
+                .allStatements()
+                + m_model->executeQuery(QString::fromLatin1("select ?g ?s ?p ?o where { graph ?g { ?s ?p ?o . } . filter(bif:exists((select (1) where { graph ?gg { ?a a %1 . ?a %2 %3 . } . ?g %4 ?gg . })))}")
+                                        .arg(Soprano::Node::resourceToN3(NAO::Agent()),
+                                             Soprano::Node::resourceToN3(NAO::identifier()),
+                                             Soprano::Node::literalToN3(QLatin1String("Otherapp")),
+                                             Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor())),
+                                        Soprano::Query::QueryLanguageSparql)
+                .iterateStatementsFromBindings(QLatin1String("s"), QLatin1String("p"), QLatin1String("o"), QLatin1String("g"))
+                .allStatements()
+                + m_model->listStatements(Node(), NAO::maintainedBy(), Node(), Node()).allStatements()
+                );
+
+    QCOMPARE(existingStatements, Soprano::Graph(m_model->listStatements().allStatements()));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// test that creating a resource by adding a property on its URI properly sets metadata
+void DataManagementModelTest::testAddProperty_createRes()
+{
+    // we create a new res by simply adding a property to it
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("Testapp"));
+
+    // now the newly created resource should have all the metadata a resource needs to have
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), NAO::created(), Node()));
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), NAO::lastModified(), Node()));
+
+    // and both created and last modification date should be similar
+    QCOMPARE(m_model->listStatements(QUrl("nepomuk:/res/A"), NAO::created(), Node()).iterateObjects().allNodes().first(),
+             m_model->listStatements(QUrl("nepomuk:/res/A"), NAO::lastModified(), Node()).iterateObjects().allNodes().first());
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+
+void DataManagementModelTest::testAddProperty_cardinality()
+{
+    // adding the same value twice in one call should result in one insert. This also includes the cardinality check
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/AA"), QUrl("prop:/res_c1"), QVariantList() << QVariant(QUrl("nepomuk:/res/B")) << QVariant(QUrl("nepomuk:/res/B")), QLatin1String("Testapp"));
+    QVERIFY(!m_dmModel->lastError());
+    QCOMPARE(m_model->listStatements(QUrl("nepomuk:/res/AA"), QUrl("prop:/res_c1"), QUrl("nepomuk:/res/B")).allStatements().count(), 1);
+
+    // we now add two values for a property with cardinality 1
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/res_c1"), QVariantList() << QVariant(QUrl("nepomuk:/res/B")) << QVariant(QUrl("nepomuk:/res/C")), QLatin1String("Testapp"));
+    QVERIFY(m_dmModel->lastError());
+
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/res_c1"), QVariantList() << QVariant(QUrl("nepomuk:/res/B")), QLatin1String("Testapp"));
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/res_c1"), QVariantList() << QVariant(QUrl("nepomuk:/res/C")), QLatin1String("Testapp"));
+
+    // the second call needs to fail
+    QVERIFY(m_dmModel->lastError());
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+
+void DataManagementModelTest::testAddProperty_file()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+    QTemporaryFile fileB;
+    fileB.open();
+    QTemporaryFile fileC;
+    fileC.open();
+
+    m_dmModel->addProperty(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/string"), QVariantList() << QVariant(QLatin1String("foobar")), QLatin1String("Testapp"));
+
+    // make sure the nie:url relation has been created
+    QVERIFY(m_model->containsAnyStatement(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())));
+    QVERIFY(!m_model->containsAnyStatement(QUrl::fromLocalFile(fileA.fileName()), Node(), Node()));
+
+    // get the resource uri
+    const QUrl fileAResUri = m_model->listStatements(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())).allStatements().first().subject().uri();
+
+    // make sure the resource is a file
+    QVERIFY(m_model->containsAnyStatement(fileAResUri, RDF::type(), NFO::FileDataObject()));
+
+    // make sure the actual value is there
+    QVERIFY(m_model->containsAnyStatement(fileAResUri, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+
+
+    // add relation from file to file
+    m_dmModel->addProperty(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/res"), QVariantList() << QVariant(QUrl::fromLocalFile(fileB.fileName())), QLatin1String("Testapp"));
+
+    // make sure the nie:url relation has been created
+    QVERIFY(m_model->containsAnyStatement(Node(), NIE::url(), QUrl::fromLocalFile(fileB.fileName())));
+    QVERIFY(!m_model->containsAnyStatement(QUrl::fromLocalFile(fileB.fileName()), Node(), Node()));
+
+    // get the resource uri
+    const QUrl fileBResUri = m_model->listStatements(Node(), NIE::url(), QUrl::fromLocalFile(fileB.fileName())).allStatements().first().subject().uri();
+
+    // make sure the resource is a file
+    QVERIFY(m_model->containsAnyStatement(fileBResUri, RDF::type(), NFO::FileDataObject()));
+
+    // make sure the actual value is there
+    QVERIFY(m_model->containsAnyStatement(fileAResUri, QUrl("prop:/res"), fileBResUri));
+
+
+    // add the same relation but with another app
+    m_dmModel->addProperty(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/res"), QVariantList() << QVariant(QUrl::fromLocalFile(fileB.fileName())), QLatin1String("Otherapp"));
+
+    // there is only one prop:/res relation defined
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/res"), Node()).allStatements().count(), 1);
+
+    // we now add two values for a property with cardinality 1
+    m_dmModel->addProperty(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/res_c1"), QVariantList() << QVariant(QUrl::fromLocalFile(fileB.fileName())), QLatin1String("Testapp"));
+    m_dmModel->addProperty(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/res_c1"), QVariantList() << QVariant(QUrl::fromLocalFile(fileC.fileName())), QLatin1String("Testapp"));
+
+    // the second call needs to fail
+    QVERIFY(m_dmModel->lastError());
+
+
+    // test adding a property to both the file and the resource URI. The result should be the exact same as doing it with only one of them
+    m_dmModel->addProperty(QList<QUrl>() << fileAResUri << QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/string"), QVariantList() << QVariant(QLatin1String("Whatever")), QLatin1String("Testapp"));
+
+    QCOMPARE(m_model->listStatements(fileAResUri, QUrl("prop:/string"), LiteralValue(QLatin1String("Whatever"))).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())).allStatements().count(), 1);
+
+    // test the same with the file as object
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/res"), QVariantList() << QVariant(KUrl(fileA.fileName())) << QVariant(fileAResUri), QLatin1String("Testapp"));
+
+    QCOMPARE(m_model->listStatements(QUrl("nepomuk:/res/A"), QUrl("prop:/res"), fileAResUri).allStatements().count(), 1);
+    QVERIFY(!m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), QUrl("prop:/res"), QUrl::fromLocalFile(fileA.fileName())));
+    QCOMPARE(m_model->listStatements(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())).allStatements().count(), 1);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testAddProperty_invalidFile()
+{
+    KTemporaryFile f1;
+    QVERIFY( f1.open() );
+    QUrl f1Url( f1.fileName() );
+    //f1Url.setScheme("file");
+
+    m_dmModel->addProperty( QList<QUrl>() << f1Url, RDF::type(), QVariantList() << NAO::Tag(), QLatin1String("testapp") );
+
+    // There should be some error that '' protocol doesn't exist
+    QVERIFY(m_dmModel->lastError());
+
+    // The support for plain file paths is in the DBus adaptor through the usage of KUrl. If
+    // local path support is neccesary on the level of the model, simply use KUrl which
+    // will automatically add the file:/ protocol to local paths.
+    QVERIFY( !m_model->containsAnyStatement( Node(), NIE::url(), f1Url ) );
+
+    m_dmModel->addProperty( QList<QUrl>() << QUrl("file:///Blah"), NIE::comment(),
+                            QVariantList() << "Comment", QLatin1String("testapp") );
+
+    // There should be some error as '/Blah' does not exist
+    QVERIFY(m_dmModel->lastError());
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testAddProperty_invalid_args()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // empty resource list
+    m_dmModel->addProperty(QList<QUrl>(), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty property uri
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("res:/A"), QUrl(), QVariantList() << 42, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty value list
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/int"), QVariantList(), QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty app
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/int"), QVariantList() << 42, QString());
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid range
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/int"), QVariantList() << QLatin1String("foobar"), QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // protected properties 1
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("res:/A"), NAO::created(), QVariantList() << QDateTime::currentDateTime(), QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // protected properties 2
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("res:/A"), NAO::lastModified(), QVariantList() << QDateTime::currentDateTime(), QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // make sure we cannot add anything to non-existing files
+    const QUrl nonExistingFileUrl("file:///a/file/that/is/very/unlikely/to/exist");
+    m_dmModel->addProperty(QList<QUrl>() << nonExistingFileUrl, QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // non-existing file as object
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/res"), QVariantList() << nonExistingFileUrl, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // TODO: try setting protected properties like nie:url, nfo:fileName, nie:isPartOf (only applies to files)
+}
+
+void DataManagementModelTest::testAddProperty_protectedTypes()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // property
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("prop:/res"), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // class
+    m_dmModel->addProperty(QList<QUrl>() << NRL::Graph(), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // graph
+    m_dmModel->addProperty(QList<QUrl>() << QUrl("graph:/onto"), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+void DataManagementModelTest::testAddProperty_akonadi()
+{
+    // create our app
+    const QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create the graph
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    QUrl resA("nepomuk:/res/A");
+    QUrl akonadiUrl("akonadi:id=5");
+    m_model->addStatement( resA, RDF::type(), NIE::DataObject(), g1 );
+    m_model->addStatement( resA, NIE::url(), akonadiUrl, g1 );
+
+    // add a property using the akonadi URL
+    // the tricky thing here is that nao:identifier does not have a range!
+    m_dmModel->addProperty( QList<QUrl>() << akonadiUrl,
+                            NAO::identifier(),
+                            QVariantList() << QString("akon"),
+                            QLatin1String("AppA") );
+
+    QVERIFY(!m_dmModel->lastError());
+
+    // check that the akonadi URL has been resolved to the resource URI
+    QVERIFY(m_model->containsAnyStatement( resA, NAO::identifier(), Soprano::Node() ));
+
+    // check that the property has the desired value
+    QVERIFY(m_model->containsAnyStatement( resA, NAO::identifier(), LiteralValue("akon") ));
+}
+
+void DataManagementModelTest::testSetProperty()
+{
+    // adding the most basic property
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/string"), QVariantList() << QVariant(QLatin1String("foobar")), QLatin1String("Testapp"));
+
+    QVERIFY(!m_dmModel->lastError());
+
+    // check that the actual data is there
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+
+    // check that the app resource has been created with its corresponding graphs
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { ?r a %1 . ?r %2 %3 . } . "
+                                                      "graph ?mg { ?g a %4 . ?mg a %5 . ?mg %6 ?g . } . }")
+                                  .arg(Soprano::Node::resourceToN3(NAO::Agent()),
+                                       Soprano::Node::resourceToN3(NAO::identifier()),
+                                       Soprano::Node::literalToN3(QLatin1String("Testapp")),
+                                       Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                                       Soprano::Node::resourceToN3(NRL::GraphMetadata()),
+                                       Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor())),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    // check that we have an InstanceBase with a GraphMetadata graph
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { <nepomuk:/res/A> <prop:/string> %1 . } . "
+                                                      "graph ?mg { ?g a %2 . ?mg a %3 . ?mg %4 ?g . } . "
+                                                      "}")
+                                  .arg(Soprano::Node::literalToN3(QLatin1String("foobar")),
+                                       Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                                       Soprano::Node::resourceToN3(NRL::GraphMetadata()),
+                                       Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor())),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    // check the number of graphs (two for the app, two for the actual data, and one for the ontology)
+    QCOMPARE(m_model->listContexts().allElements().count(), 5);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// test that creating a resource by setting a property on its URI properly sets metadata
+void DataManagementModelTest::testSetProperty_createRes()
+{
+    // we create a new res by simply adding a property to it
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("Testapp"));
+
+    // now the newly created resource should have all the metadata a resource needs to have
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), NAO::created(), Node()));
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), NAO::lastModified(), Node()));
+
+    // and both created and last modification date should be similar
+    QCOMPARE(m_model->listStatements(QUrl("nepomuk:/res/A"), NAO::created(), Node()).iterateObjects().allNodes().first(),
+             m_model->listStatements(QUrl("nepomuk:/res/A"), NAO::lastModified(), Node()).iterateObjects().allNodes().first());
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+
+void DataManagementModelTest::testSetProperty_overwrite()
+{
+    // create an app graph
+    const QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // add a resource with 2 properties
+    QUrl mg;
+    const QUrl g = m_nrlModel->createGraph(NRL::InstanceBase(), &mg);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+
+    m_model->addStatement(g, NAO::maintainedBy(), QUrl("app:/A"), mg);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/A"), mg2);
+
+    m_model->addStatement(QUrl("res:/A"), RDF::type(), NAO::Tag(), g);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42), g);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(42), g);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int3"), LiteralValue(42), g2);
+
+    QVERIFY(!haveTrailingGraphs());
+
+
+    //
+    // now overwrite the one property
+    //
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/int"), QVariantList() << 12, QLatin1String("testapp"));
+
+    // now the model should have replaced the old value and added the new value in a new graph
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(12)));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42)));
+
+    // a new graph
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(12)).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(42)).allStatements().count(), 1);
+    QVERIFY(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(12)).allStatements().first().context().uri() != g);
+    QVERIFY(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(42)).allStatements().first().context().uri() == g);
+
+    // the testapp Agent as maintainer of the new graph
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { <res:/A> <prop:/int> %1 . } . "
+                                                      "graph ?mg { ?g a %2 . ?mg a %3 . ?mg %4 ?g . } . "
+                                                      "?g %5 ?a . ?a %6 %7 . "
+                                                      "}")
+                                  .arg(Soprano::Node::literalToN3(12),
+                                       Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                                       Soprano::Node::resourceToN3(NRL::GraphMetadata()),
+                                       Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()),
+                                       Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                       Soprano::Node::resourceToN3(NAO::identifier()),
+                                       Soprano::Node::literalToN3(QLatin1String("testapp"))),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    QVERIFY(!haveTrailingGraphs());
+
+
+    //
+    // Rewrite the same value
+    //
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/int2"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // the value should only be there once
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(42)).allStatements().count(), 1);
+
+    // in a new graph since the old one still contains the type
+    QVERIFY(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(42)).allStatements().first().context().uri() != g);
+
+    // there should be one graph now which contains the value and which is marked as being maintained by both apps
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { <res:/A> <prop:/int2> %1 . } . "
+                                                      "graph ?mg { ?g a %2 . ?mg a %3 . ?mg %4 ?g . } . "
+                                                      "?g %5 ?a1 . ?a1 %6 %7 . "
+                                                      "?g %5 ?a2 . ?a2 %6 %8 . "
+                                                      "}")
+                                  .arg(Soprano::Node::literalToN3(42),
+                                       Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                                       Soprano::Node::resourceToN3(NRL::GraphMetadata()),
+                                       Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()),
+                                       Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                       Soprano::Node::resourceToN3(NAO::identifier()),
+                                       Soprano::Node::literalToN3(QLatin1String("testapp")),
+                                       Soprano::Node::literalToN3(QLatin1String("A"))),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    //
+    // Now we rewrite the type which should result in reusing the old graph but with the new app as maintainer
+    //
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("res:/A"), RDF::type(), QVariantList() << NAO::Tag(), QLatin1String("testapp"));
+
+    // the type should only be define once
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), RDF::type(), NAO::Tag()).allStatements().count(), 1);
+
+    // the graph should be the same
+    QVERIFY(m_model->listStatements(QUrl("res:/A"), RDF::type(), NAO::Tag()).allStatements().first().context().uri() == g);
+
+    // the new app should be listed as maintainer as should be the old one
+    QCOMPARE(m_model->listStatements(g, NAO::maintainedBy(), Node(), mg).allStatements().count(), 2);
+
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { <res:/A> a %1 . } . "
+                                                      "graph ?mg { ?g a %2 . ?mg a %3 . ?mg %4 ?g . } . "
+                                                      "?g %5 ?a1 . ?a1 %6 %7 . "
+                                                      "?g %5 ?a2 . ?a2 %6 %8 . "
+                                                      "}")
+                                  .arg(Soprano::Node::resourceToN3(NAO::Tag()),
+                                       Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                                       Soprano::Node::resourceToN3(NRL::GraphMetadata()),
+                                       Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()),
+                                       Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                       Soprano::Node::resourceToN3(NAO::identifier()),
+                                       Soprano::Node::literalToN3(QLatin1String("testapp")),
+                                       Soprano::Node::literalToN3(QLatin1String("A"))),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testSetProperty_invalid_args()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // empty resource list
+    m_dmModel->setProperty(QList<QUrl>(), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty property uri
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl(), QVariantList() << 42, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty value list
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/int"), QVariantList(), QLatin1String("testapp"));
+
+    // the call should NOT have failed
+    QVERIFY(!m_dmModel->lastError());
+
+    // but nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty app
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/int"), QVariantList() << 42, QString());
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid range
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/int"), QVariantList() << QLatin1String("foobar"), QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // make sure we cannot add anything to non-existing files
+    const QUrl nonExistingFileUrl("file:///a/file/that/is/very/unlikely/to/exist");
+    m_dmModel->setProperty(QList<QUrl>() << nonExistingFileUrl, QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // non-existing file as object
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), QUrl("prop:/res"), QVariantList() << nonExistingFileUrl, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+void DataManagementModelTest::testSetProperty_nieUrl1()
+{
+    // setting nie:url if it is not there yet should result in a normal setProperty including graph creation
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), NIE::url(), QVariantList() << QUrl("file:///tmp/A"), QLatin1String("testapp"));
+
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), NIE::url(), QUrl("file:///tmp/A")));
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), RDF::type(), NFO::FileDataObject()));
+
+    // remember the graph since it should not change later on
+    const QUrl nieUrlGraph = m_model->listStatements(QUrl("nepomuk:/res/A"), NIE::url(), QUrl("file:///tmp/A")).allStatements().first().context().uri();
+
+    QVERIFY(!haveTrailingGraphs());
+
+
+    // we reset the URL
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("nepomuk:/res/A"), NIE::url(), QVariantList() << QUrl("file:///tmp/B"), QLatin1String("testapp"));
+
+    // the url should have changed
+    QVERIFY(!m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), NIE::url(), QUrl("file:///tmp/A")));
+    QVERIFY(m_model->containsAnyStatement(QUrl("nepomuk:/res/A"), NIE::url(), QUrl("file:///tmp/B")));
+
+    // the graph should have been kept
+    QCOMPARE(m_model->listStatements(QUrl("nepomuk:/res/A"), NIE::url(), Node()).allStatements().first().context().uri(), nieUrlGraph);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testSetProperty_nieUrl2()
+{
+    KTempDir* dir = createNieUrlTestData();
+
+    // change the nie:url of one of the top level dirs
+    const QUrl newDir1Url = QUrl(QLatin1String("file://") + dir->name() + QLatin1String("dir1-new"));
+
+    // we first need to move the file, otherwise the file check in the dms kicks in
+    QVERIFY(QFile::rename(dir->name() + QLatin1String("dir1"), newDir1Url.toLocalFile()));
+
+    // now update the database
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("res:/dir1"), NIE::url(), QVariantList() << newDir1Url, QLatin1String("testapp"));
+
+    // this should have updated the nie:urls of all children, too
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir1"), NIE::url(), newDir1Url));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir1"), NFO::fileName(), LiteralValue(QLatin1String("dir1-new"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir11"), NIE::url(), QUrl(newDir1Url.toString() + QLatin1String("/dir11"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir12"), NIE::url(), QUrl(newDir1Url.toString() + QLatin1String("/dir12"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir13"), NIE::url(), QUrl(newDir1Url.toString() + QLatin1String("/dir13"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/file11"), NIE::url(), QUrl(newDir1Url.toString() + QLatin1String("/file11"))));
+
+    delete dir;
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// the same test as above only using the file URL
+void DataManagementModelTest::testSetProperty_nieUrl3()
+{
+    KTempDir* dir = createNieUrlTestData();
+
+    // change the nie:url of one of the top level dirs
+    const QUrl oldDir1Url = QUrl(QLatin1String("file://") + dir->name() + QLatin1String("dir1"));
+    const QUrl newDir1Url = QUrl(QLatin1String("file://") + dir->name() + QLatin1String("dir1-new"));
+
+    // we first need to move the file, otherwise the file check in the dms kicks in
+    QVERIFY(QFile::rename(oldDir1Url.toLocalFile(), newDir1Url.toLocalFile()));
+
+    // now update the database
+    m_dmModel->setProperty(QList<QUrl>() << oldDir1Url, NIE::url(), QVariantList() << newDir1Url, QLatin1String("testapp"));
+
+    // this should have updated the nie:urls of all children, too
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir1"), NIE::url(), newDir1Url));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir1"), NFO::fileName(), LiteralValue(QLatin1String("dir1-new"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir11"), NIE::url(), QUrl(newDir1Url.toString() + QLatin1String("/dir11"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir12"), NIE::url(), QUrl(newDir1Url.toString() + QLatin1String("/dir12"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir13"), NIE::url(), QUrl(newDir1Url.toString() + QLatin1String("/dir13"))));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/file11"), NIE::url(), QUrl(newDir1Url.toString() + QLatin1String("/file11"))));
+
+    delete dir;
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testSetProperty_nieUrl4()
+{
+    KTempDir* dir = createNieUrlTestData();
+
+    // move one of the dirs to a new parent
+    const QUrl oldDir121Url = QUrl(QLatin1String("file://") + dir->name() + QLatin1String("dir1/dir12/dir121"));
+    const QUrl newDir121Url = QUrl(QLatin1String("file://") + dir->name() + QLatin1String("dir1/dir12/dir121-new"));
+
+    // we first need to move the file, otherwise the file check in the dms kicks in
+    QVERIFY(QFile::rename(oldDir121Url.toLocalFile(), newDir121Url.toLocalFile()));
+
+    // now update the database
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("res:/dir121"), NIE::url(), QVariantList() << newDir121Url, QLatin1String("testapp"));
+
+    // the url
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir121"), NIE::url(), newDir121Url));
+
+    // the child file
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/file1211"), NIE::url(), QUrl(newDir121Url.toString() + QLatin1String("/file1211"))));
+
+    // the nie:isPartOf relationship should have been updated, too
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir121"), NIE::isPartOf(), QUrl("res:/dir12")));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// the same test as above only using the file URL
+void DataManagementModelTest::testSetProperty_nieUrl5()
+{
+    KTempDir* dir = createNieUrlTestData();
+
+    // move one of the dirs to a new parent
+    const QUrl oldDir121Url = QUrl(QLatin1String("file://") + dir->name() + QLatin1String("dir1/dir12/dir121"));
+    const QUrl newDir121Url = QUrl(QLatin1String("file://") + dir->name() + QLatin1String("dir2/dir121"));
+
+    // we first need to move the file, otherwise the file check in the dms kicks in
+    QVERIFY(QFile::rename(oldDir121Url.toLocalFile(), newDir121Url.toLocalFile()));
+
+    // now update the database
+    m_dmModel->setProperty(QList<QUrl>() << oldDir121Url, NIE::url(), QVariantList() << newDir121Url, QLatin1String("testapp"));
+
+    // the url
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir121"), NIE::url(), newDir121Url));
+
+    // the child file
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/file1211"), NIE::url(), QUrl(newDir121Url.toString() + QLatin1String("/file1211"))));
+
+    // the nie:isPartOf relationship should have been updated, too
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/dir121"), NIE::isPartOf(), QUrl("res:/dir2")));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// test support for any other URL scheme which already exists as nie:url (This is what libnepomuk does support)
+void DataManagementModelTest::testSetProperty_nieUrl6()
+{
+    // create a resource that has a URL
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+    const QUrl url("http://nepomuk.kde.org/");
+
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), url, g1);
+
+
+    // use the url to set a property
+    m_dmModel->setProperty(QList<QUrl>() << url, QUrl("prop:/int"), QVariantList() << 42, QLatin1String("A"));
+
+    // check that the property has been added to the resource
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42)));
+
+    // check that no new resource has been created
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/int"), LiteralValue(42)).allElements().count(), 1);
+}
+
+void DataManagementModelTest::testSetProperty_protectedTypes()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // property
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("prop:/res"), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // class
+    m_dmModel->setProperty(QList<QUrl>() << NRL::Graph(), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // graph
+    m_dmModel->setProperty(QList<QUrl>() << QUrl("graph:/onto"), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+// make sure we reuse legacy resource URIs
+void DataManagementModelTest::testSetProperty_legacyData()
+{
+    // create some legacy data
+    QTemporaryFile file;
+    file.open();
+    const KUrl url(file.fileName());
+
+    const QUrl g = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    m_model->addStatement(url, QUrl("prop:/int"), LiteralValue(42), g);
+
+    // set some data with the url
+    m_dmModel->setProperty(QList<QUrl>() << url, QUrl("prop:/int"), QVariantList() << 2, QLatin1String("A"));
+
+    // make sure the resource has changed
+    QCOMPARE(m_model->listStatements(url, QUrl("prop:/int"), Node()).allElements().count(), 1);
+    QCOMPARE(m_model->listStatements(url, QUrl("prop:/int"), Node()).allElements().first().object().literal(), LiteralValue(2));
+}
+
+void DataManagementModelTest::testRemoveProperty()
+{
+    const int cleanCount = m_model->statementCount();
+
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/string"), QVariantList() << QLatin1String("hello world"), QLatin1String("Testapp"));
+
+    QVERIFY(!m_dmModel->lastError());
+
+    // test that the data has been removed
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world"))));
+
+    // test that the mtime has been updated (and is thus in another graph)
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), NAO::lastModified(), Soprano::Node(), g1));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NAO::lastModified(), Soprano::Node()));
+
+    // test that the other property value is still valid
+    QVERIFY(m_model->containsStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1));
+
+    QVERIFY(!haveTrailingGraphs());
+
+
+    // step 2: remove the second value
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/string"), QVariantList() << QLatin1String("foobar"), QLatin1String("Testapp"));
+
+    QVERIFY(!m_dmModel->lastError());
+
+    // the property should be gone entirely
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), Soprano::Node()));
+
+    // even the resource should be gone since the NAO mtime does not count as a "real" property
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Soprano::Node(), Soprano::Node()));
+
+    // nothing except the ontology and the Testapp Agent should be left
+    QCOMPARE(m_model->statementCount(), cleanCount+6);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testRemoveProperty_file()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+    QTemporaryFile fileB;
+    fileB.open();
+
+    // prepare some test data
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("whatever")), g1);
+
+    m_model->addStatement(QUrl("res:/B"), NIE::url(), QUrl::fromLocalFile(fileB.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/C"), g1);
+
+
+
+    // now we remove one value via the file URL
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/string"), QVariantList() << QLatin1String("hello world"), QLatin1String("Testapp"));
+
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/string"), Node()).allStatements().count(), 2);
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), NIE::url(), Node()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName())).allStatements().count(), 1);
+
+
+    // test the same with a file URL value
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/res"), QVariantList() << QUrl::fromLocalFile(fileB.fileName()), QLatin1String("Testapp"));
+
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), QUrl("prop:/res"), Node()).allStatements().count(), 1);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testRemoveProperty_invalid_args()
+{
+    // prepare some test data
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // empty resource list
+    m_dmModel->removeProperty(QList<QUrl>(), QUrl("prop:/string"), QVariantList() << QLatin1String("foobar"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // resource list with empty URL
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl() << QUrl("res:/A"), QUrl("prop:/string"), QVariantList() << QLatin1String("foobar"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty property
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl(), QVariantList() << QLatin1String("foobar"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty values
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/string"), QVariantList(), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty app
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/string"), QVariantList() << QLatin1String("foobar"), QString());
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid value type
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/int"), QVariantList() << QLatin1String("foobar"), QString("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // protected property 1
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), NAO::created(), QVariantList() << QDateTime::currentDateTime(), QString("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // protected property 2
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), NAO::lastModified(), QVariantList() << QDateTime::currentDateTime(), QString("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // make sure we cannot add anything to non-existing files
+    const QUrl nonExistingFileUrl("file:///a/file/that/is/very/unlikely/to/exist");
+    m_dmModel->removeProperty(QList<QUrl>() << nonExistingFileUrl, QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // non-existing file as object
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("res:/A"), QUrl("prop:/res"), QVariantList() << nonExistingFileUrl, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+// it is not allowed to change properties, classes or graphs through this API
+void DataManagementModelTest::testRemoveProperty_protectedTypes()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // property
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("prop:/res"), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // class
+    m_dmModel->removeProperty(QList<QUrl>() << NRL::Graph(), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // graph
+    m_dmModel->removeProperty(QList<QUrl>() << QUrl("graph:/onto"), QUrl("prop:/int"), QVariantList() << 42, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+void DataManagementModelTest::testRemoveProperties()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+    QTemporaryFile fileB;
+    fileB.open();
+
+    // prepare some test data
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("whatever")), g1);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(2), g1);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(2), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("whatever")), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(6), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(2), g1);
+
+    m_model->addStatement(QUrl("res:/B"), NIE::url(), QUrl::fromLocalFile(fileB.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/C"), g1);
+
+
+    // test removing one property from one resource
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("res:/A"), QList<QUrl>() << QUrl("prop:/string"), QLatin1String("testapp"));
+
+    // check that all values are gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), Node()));
+
+    // check that all other values from that prop are still there
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/string"), Node()).allStatements().count(), 3);
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), QUrl("prop:/string"), Node()).allStatements().count(), 3);
+
+
+    // test removing a property from more than one resource
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("res:/A") << QUrl("res:/B"), QList<QUrl>() << QUrl("prop:/int"), QLatin1String("testapp"));
+
+    // check that all values are gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int"), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), QUrl("prop:/int"), Node()));
+
+    // check that other properties from res:/B are still there
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), QUrl("prop:/string"), Node()).allStatements().count(), 3);
+
+
+    // test file URLs in the resources
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), QList<QUrl>() << QUrl("prop:/int2"), QLatin1String("testapp"));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/int2"), Node()));
+
+    // TODO: verify graphs
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+
+void DataManagementModelTest::testRemoveProperties_invalid_args()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+    QTemporaryFile fileB;
+    fileB.open();
+
+    // prepare some test data
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("whatever")), g1);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int"), LiteralValue(2), g1);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/int2"), LiteralValue(2), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("whatever")), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(6), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(12), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(2), g1);
+
+    m_model->addStatement(QUrl("res:/B"), NIE::url(), QUrl::fromLocalFile(fileB.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/C"), g1);
+
+
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // empty resource list
+    m_dmModel->removeProperties(QList<QUrl>(), QList<QUrl>() << QUrl("prop:/string"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // resource list with empty url
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl() << QUrl("res:/A"), QList<QUrl>() << QUrl("prop:/string"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty property list
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("res:/A"), QList<QUrl>(), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // property list with empty url
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("res:/A"), QList<QUrl>() << QUrl("prop:/string") << QUrl(), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty app
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("res:/A"), QList<QUrl>() << QUrl("prop:/string"), QString());
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // protected property 1
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("res:/A"), QList<QUrl>() << NAO::created(), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // protected property 2
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("res:/A"), QList<QUrl>() << NAO::lastModified(), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // make sure we cannot add anything to non-existing files
+    const QUrl nonExistingFileUrl("file:///a/file/that/is/very/unlikely/to/exist");
+    m_dmModel->removeProperties(QList<QUrl>() << nonExistingFileUrl, QList<QUrl>() << QUrl("prop:/int"), QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+
+void DataManagementModelTest::testRemoveProperties_protectedTypes()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // property
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("prop:/res"), QList<QUrl>() << QUrl("prop:/int"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // class
+    m_dmModel->removeProperties(QList<QUrl>() << NRL::Graph(), QList<QUrl>() << QUrl("prop:/int"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // graph
+    m_dmModel->removeProperties(QList<QUrl>() << QUrl("graph:/onto"), QList<QUrl>() << QUrl("prop:/int"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);}
+
+void DataManagementModelTest::testRemoveResources()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+    QTemporaryFile fileB;
+    fileB.open();
+
+    // prepare some test data
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("whatever")), g1);
+
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(6), g2);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/int"), LiteralValue(12), g2);
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/int"), LiteralValue(2), g2);
+    m_model->addStatement(QUrl("res:/B"), NIE::url(), QUrl::fromLocalFile(fileB.fileName()), g2);
+
+
+    m_dmModel->removeResources(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("testapp"));
+
+    // verify that the resource is gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+
+    // verify that other resources were not touched
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), Node(), Node()).allStatements().count(), 4);
+    QCOMPARE(m_model->listStatements(QUrl("res:/C"), Node(), Node()).allStatements().count(), 2);
+
+    // verify that removing resources by file URL works
+    m_dmModel->removeResources(QList<QUrl>() << QUrl::fromLocalFile(fileB.fileName()), Nepomuk::NoRemovalFlags, QLatin1String("testapp"));
+
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), Node(), Node()));
+
+    // verify that other resources were not touched
+    QCOMPARE(m_model->listStatements(QUrl("res:/C"), Node(), Node()).allStatements().count(), 2);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testRemoveResources_subresources()
+{
+    // create our apps (we use more than one since we also test that it is ignored)
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // create the graphs
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+    QUrl mg3;
+    const QUrl g3 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg3);
+    m_model->addStatement(g3, NAO::maintainedBy(), QUrl("app:/A"), mg3);
+
+    // create the resource to delete
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+
+    // sub-resource 1: can be deleted
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // sub-resource 2: can be deleted (is defined in another graph by the same app)
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/AA"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/AA"), g1);
+    m_model->addStatement(QUrl("res:/AA"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g3);
+
+    // sub-resource 3: can be deleted although another res refs it (we also delete the other res)
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/C"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/C"), g1);
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/res"), QUrl("res:/C"), g1);
+
+    // sub-resource 4: cannot be deleted since another res refs it
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/D"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/E"), QUrl("prop:/res"), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/E"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // sub-resource 5: can be deleted although another app added properties
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/F"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/F"), g1);
+    m_model->addStatement(QUrl("res:/F"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/F"), QUrl("prop:/int"), LiteralValue(42), g2);
+
+    // delete the resource
+    m_dmModel->removeResources(QList<QUrl>() << QUrl("res:/A"), Nepomuk::RemoveSubResoures, QLatin1String("A"));
+
+    // this should have removed A, B and C
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/C"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/F"), Node(), Node()));
+
+    // E and F need to be preserved
+    QCOMPARE(m_model->listStatements(QUrl("res:/D"), Node(), Node()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(QUrl("res:/E"), Node(), Node()).allStatements().count(), 2);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testRemoveResources_invalid_args()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+
+    // prepare some test data
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("whatever")), g1);
+
+
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // empty resource list
+    m_dmModel->removeResources(QList<QUrl>(), Nepomuk::NoRemovalFlags, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // resource list with empty URL
+    m_dmModel->removeResources(QList<QUrl>() << QUrl("res:/A") << QUrl(), Nepomuk::NoRemovalFlags, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty app
+    m_dmModel->removeResources(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QString());
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // non-existing file
+    const QUrl nonExistingFileUrl("file:///a/file/that/is/very/unlikely/to/exist");
+    m_dmModel->removeResources(QList<QUrl>() << nonExistingFileUrl, Nepomuk::NoRemovalFlags, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+// make sure we do not allow to remove classes, properties, and graphs
+void DataManagementModelTest::testRemoveResources_protectedTypes()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // property
+    m_dmModel->removeResources(QList<QUrl>() << QUrl("prop:/res"), Nepomuk::NoRemovalFlags, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // class
+    m_dmModel->removeResources(QList<QUrl>() << NRL::Graph(), Nepomuk::NoRemovalFlags, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // graph
+    m_dmModel->removeResources(QList<QUrl>() << QUrl("graph:/onto"), Nepomuk::NoRemovalFlags, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+// make sure the mtime of related resources is updated properly
+void DataManagementModelTest::testRemoveResources_mtimeRelated()
+{
+    // first we create our apps and graphs (just to have some pseudo real data)
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    const QDateTime date = QDateTime::currentDateTime();
+
+
+    // now we create different resources
+    // A is the resource to be deleted
+    // B is related to A and its mtime needs update
+    // C is unrelated and no mtime change should occur
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::lastModified(), LiteralValue(date), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+    m_model->addStatement(QUrl("res:/B"), NAO::created(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/B"), NAO::lastModified(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/res"), QUrl("res:/A"), g2);
+
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+    m_model->addStatement(QUrl("res:/C"), NAO::created(), LiteralValue(date), g2);
+    m_model->addStatement(QUrl("res:/C"), NAO::lastModified(), LiteralValue(date), g2);
+
+
+    // now we remove res:/A
+    m_dmModel->removeResources(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+
+    // now only the mtime of B should have changed
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), NAO::lastModified(), Node()).allElements().count(), 1);
+    QVERIFY(m_model->listStatements(QUrl("res:/B"), NAO::lastModified(), Node()).allElements().first().object().literal().toDateTime() > date);
+
+    QCOMPARE(m_model->listStatements(QUrl("res:/C"), NAO::lastModified(), Node()).allElements().count(), 1);
+    QCOMPARE(m_model->listStatements(QUrl("res:/C"), NAO::lastModified(), Node()).allElements().first().object().literal().toDateTime(), date);
+}
+
+// make sure we can remove data from non-existing files
+void DataManagementModelTest::testRemoveResources_deletedFile()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+
+    const KUrl fileUrl(fileA.fileName());
+
+    // create our app
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create the data graph
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    // create the resource
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), fileUrl, g1);
+    m_model->addStatement(QUrl("res:/A"), RDF::type(), NFO::FileDataObject(), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // now remove the file
+    fileA.close();
+    QFile::remove(fileUrl.toLocalFile());
+
+    // now try removing the data
+    m_dmModel->removeResources(QList<QUrl>() << fileUrl, NoRemovalFlags, QLatin1String("A"));
+
+    // the call should succeed
+    QVERIFY(!m_dmModel->lastError());
+
+    // the resource should be gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(Node(), Node(), fileUrl));
+}
+
+void DataManagementModelTest::testCreateResource()
+{
+    // the simple test: we just create a resource using all params
+    const QUrl resUri = m_dmModel->createResource(QList<QUrl>() << QUrl("class:/typeA") << QUrl("class:/typeB"), QLatin1String("the label"), QLatin1String("the desc"), QLatin1String("A"));
+
+    // this call should succeed
+    QVERIFY(!m_dmModel->lastError());
+
+    // check if the returned uri is valid
+    QVERIFY(!resUri.isEmpty());
+    QCOMPARE(resUri.scheme(), QString(QLatin1String("nepomuk")));
+
+    // check if the resource was created properly
+    QVERIFY(m_model->containsAnyStatement(resUri, RDF::type(), QUrl("class:/typeA")));
+    QVERIFY(m_model->containsAnyStatement(resUri, RDF::type(), QUrl("class:/typeB")));
+    QVERIFY(m_model->containsAnyStatement(resUri, NAO::prefLabel(), LiteralValue::createPlainLiteral(QLatin1String("the label"))));
+    QVERIFY(m_model->containsAnyStatement(resUri, NAO::description(), LiteralValue::createPlainLiteral(QLatin1String("the desc"))));
+}
+
+void DataManagementModelTest::testCreateResource_invalid_args()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // try to create a resource without any types
+    m_dmModel->createResource(QList<QUrl>(), QString(), QString(), QLatin1String("A"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // use an invalid type
+    m_dmModel->createResource(QList<QUrl>() << QUrl("class:/non-existing-type"), QString(), QString(), QLatin1String("A"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // use a property as type
+    m_dmModel->createResource(QList<QUrl>() << NAO::prefLabel(), QString(), QString(), QLatin1String("A"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+// the isolated test: create one graph with one resource, delete that resource
+void DataManagementModelTest::testRemoveDataByApplication1()
+{
+    // create our app
+    const QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // verify that nothing is left, not even the graph
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(Node(), NAO::maintainedBy(), QUrl("app:/A")));
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NRL::InstanceBase()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NRL::GraphMetadata()).allStatements().count(), 1);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// scatter resource over two graphs, only one of which is supposed to be removed
+void DataManagementModelTest::testRemoveDataByApplication2()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // verify that graph1 is gone completely
+    QVERIFY(!m_model->containsAnyStatement(Node(), Node(), Node(), g1));
+
+    // only two statements left: the one in the second graph and the last modification date
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), Node(), Node()).allStatements().count(), 2);
+    QVERIFY(m_model->containsStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NAO::lastModified(), Node()));
+
+    // four graphs: g2, the 2 app graphs, and the mtime graph
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NRL::InstanceBase()).allStatements().count(), 4);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// two apps that maintain a graph should keep the data when one removes it
+void DataManagementModelTest::testRemoveDataByApplication3()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/B"), mg1);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // the resource should still be there, without any changes, not even a changed mtime
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), Node(), Node()).allStatements().count(), 2);
+
+    QVERIFY(!m_model->containsAnyStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1));
+    QCOMPARE(m_model->listStatements(g1, NAO::maintainedBy(), Node()).allStatements().count(), 1);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// test file URLs + not removing nie:url
+void DataManagementModelTest::testRemoveDataByApplication4()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // now the nie:url should still be there even though A created it
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName())));
+
+    // creation time should have been created
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NAO::lastModified(), Node()));
+
+    // the foobar value should be gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+
+    // the "hello world" should still be there
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world"))));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// test sub-resource handling the easy kind
+void DataManagementModelTest::testRemoveDataByApplication5()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::RemoveSubResoures, QLatin1String("A"));
+
+    // this should have removed both A and B
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), Node(), Node()));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// test sub-resource handling
+void DataManagementModelTest::testRemoveDataByApplication6()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // create the graphs
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+    QUrl mg3;
+    const QUrl g3 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg3);
+    m_model->addStatement(g3, NAO::maintainedBy(), QUrl("app:/A"), mg3);
+
+    // create the resource to delete
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+
+    // sub-resource 1: can be deleted
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // sub-resource 2: can be deleted (is defined in another graph by the same app)
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/AA"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/AA"), g1);
+    m_model->addStatement(QUrl("res:/AA"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g3);
+
+    // sub-resource 3: can be deleted although another res refs it (we also delete the other res)
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/C"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/C"), g1);
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/res"), QUrl("res:/C"), g1);
+
+    // sub-resource 4: cannot be deleted since another res refs it
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/D"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/E"), QUrl("prop:/res"), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/E"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // sub-resource 5: cannot be deleted since another app added properties
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/F"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/F"), g1);
+    m_model->addStatement(QUrl("res:/F"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/F"), QUrl("prop:/int"), LiteralValue(42), g2);
+
+    // sub-resource 6: can be deleted since the prop another app added is only metadata
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/G"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/G"), g1);
+    m_model->addStatement(QUrl("res:/G"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/G"), NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g2);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::RemoveSubResoures, QLatin1String("A"));
+
+    // this should have removed A, B and C
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/C"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/G"), Node(), Node()));
+
+    // E and F need to be preserved
+    QCOMPARE(m_model->listStatements(QUrl("res:/D"), Node(), Node()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(QUrl("res:/E"), Node(), Node()).allStatements().count(), 2);
+    QCOMPARE(m_model->listStatements(QUrl("res:/F"), Node(), Node()).allStatements().count(), 2);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// make sure that we do not remove metadata from resources that were also touched by other apps
+void DataManagementModelTest::testRemoveDataByApplication7()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // verify that the creation date is still there
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NAO::created(), Soprano::Node()));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// make sure everything is removed even if splitted in more than one graph
+void DataManagementModelTest::testRemoveDataByApplication8()
+{
+    // create our app
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/A"), mg2);
+
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // verify that all has gone
+    // verify that nothing is left, not even the graph
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(Node(), NAO::maintainedBy(), QUrl("app:/A")));
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NRL::InstanceBase()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NRL::GraphMetadata()).allStatements().count(), 1);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// make sure that we still maintain other resources in the same graph after deleting one resource
+void DataManagementModelTest::testRemoveDataByApplication9()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // create the graph
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/B"), mg1);
+
+    // create the resources
+    const QDateTime dt = QDateTime::currentDateTime();
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(dt), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/B"), NAO::created(), LiteralValue(dt), g1);
+
+    // now remove res:/A by app A
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // now there should be 2 graphs - once for res:/A which is only maintained by B, and one for res:/B which is still
+    // maintained by A and B
+    // 1. check that B still maintains res:/A (all of it in one graph)
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { graph ?g { <res:/A> <prop:/string> %1 . <res:/A> %2 %3 . } . ?g %4 <app:/B> . }")
+                                  .arg(Soprano::Node::literalToN3(LiteralValue(QLatin1String("foobar"))),
+                                       Soprano::Node::resourceToN3(NAO::created()),
+                                       Soprano::Node::literalToN3(LiteralValue(dt)),
+                                       Soprano::Node::resourceToN3(NAO::maintainedBy())),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    // 2. check that A does not maintain res:/A anymore
+    QVERIFY(!m_model->executeQuery(QString::fromLatin1("ask where { graph ?g { <res:/A> ?p ?o } . ?g %1 <app:/A> . }")
+                                  .arg(Soprano::Node::resourceToN3(NAO::maintainedBy())),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    // 3. check that both A and B do still maintain res:/B
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { graph ?g { <res:/B> <prop:/string> %1 . <res:/B> %2 %3 . } . ?g %4 <app:/A> . ?g %4 <app:/B> . }")
+                                  .arg(Soprano::Node::literalToN3(LiteralValue(QLatin1String("hello world"))),
+                                       Soprano::Node::resourceToN3(NAO::created()),
+                                       Soprano::Node::literalToN3(LiteralValue(dt)),
+                                       Soprano::Node::resourceToN3(NAO::maintainedBy())),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+}
+
+// This test simply creates a lot of resources using storeResources and then
+// removes all of them using removeDataByApplication.
+// This is exactly what the strigi service does.
+void DataManagementModelTest::testRemoveDataByApplication10()
+{
+    QLatin1String app("AppA");
+    QList<QUrl> uris;
+
+    for( int i=0; i<10; i++ ) {
+        QTemporaryFile fileA;
+        fileA.open();
+
+        SimpleResource res;
+        res.addProperty( RDF::type(), NFO::FileDataObject() );
+        res.addProperty( NIE::url(), QUrl(fileA.fileName()) );
+
+        m_dmModel->storeResources( SimpleResourceGraph() << res, app );
+        QVERIFY( !m_dmModel->lastError() );
+
+        QString query = QString::fromLatin1("select ?r where { ?r %1 %2 . }")
+                        .arg( Node::resourceToN3( NIE::url() ),
+                            Node::resourceToN3( QUrl(fileA.fileName()) ) );
+
+        QList<Node> list = m_dmModel->executeQuery( query, Soprano::Query::QueryLanguageSparql ).iterateBindings(0).allNodes();
+        QCOMPARE( list.size(), 1 );
+
+        uris << list.first().uri();
+    }
+
+    //
+    // Remove the data
+    //
+
+    m_dmModel->removeDataByApplication( uris, Nepomuk::RemoveSubResoures,
+                                        QLatin1String("AppA") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    QString query = QString::fromLatin1("ask where { graph ?g { ?r ?p ?o . } ?g %1 ?app . ?app %2 %3 . }")
+                    .arg( Node::resourceToN3( NAO::maintainedBy() ),
+                          Node::resourceToN3( NAO::identifier() ),
+                          Node::literalToN3( app ) );
+
+    QVERIFY( !m_dmModel->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue() );
+
+    foreach( const QUrl resUri, uris ) {
+
+        // The Resource should no longer have any statements
+        QList<Soprano::Statement> l = m_dmModel->listStatements( resUri, Node(), Node() ).allStatements();
+        QVERIFY( l.isEmpty() );
+    }
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// make sure that graphs which do not have a maintaining app are handled properly, too
+void DataManagementModelTest::testRemoveDataByApplication11()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+
+    // create our app
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create the resource to delete
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    // graph 2 does not have a maintaining app!
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName()), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+
+    // delete the resource
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl::fromLocalFile(fileA.fileName()), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // now the nie:url should still be there even though A created it
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NIE::url(), QUrl::fromLocalFile(fileA.fileName())));
+
+    // creation time should have been created
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NAO::lastModified(), Node()));
+
+    // the foobar value should be gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+
+    // the "hello world" should still be there
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world"))));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// make sure that weird cross sub-resource'ing is handled properly. This is very unlikely to ever happen, but still...
+void DataManagementModelTest::testRemoveDataByApplication_subResourcesOfSubResources()
+{
+    // create our app
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create the graph
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    // create the resource to delete
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+
+    // sub-resource 1
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // sub-resource 2
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/C"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/C"), g1);
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // sub-resource 3 (also sub-resource to res:/C)
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/res"), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/C"), NAO::hasSubResource(), QUrl("res:/D"), g1);
+    m_model->addStatement(QUrl("res:/D"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+
+    // delete the resource
+    QBENCHMARK_ONCE
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::RemoveSubResoures, QLatin1String("A"));
+
+
+    // all resources should have been removed
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/C"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/D"), Node(), Node()));
+}
+
+// This is some real data that I have in my nepomuk repo
+void DataManagementModelTest::testRemoveDataByApplication_realLife()
+{
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/c07bb72a-6aec-450a-9622-fe8f05f83d79>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/c07bb72a-6aec-450a-9622-fe8f05f83d79>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/c07bb72a-6aec-450a-9622-fe8f05f83d79>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/c07bb72a-6aec-450a-9622-fe8f05f83d79>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#lastModified>"),Soprano::Node::fromN3("\"2011-05-24T10:35:39.414Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/1b99f767-3652-4bb5-97a5-dcb469ffa186>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/c07bb72a-6aec-450a-9622-fe8f05f83d79>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#hasTag>"),Soprano::Node::fromN3("<nepomuk:/res/8e251223-a066-4c90-863f-2f49237c870f>"),Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/c07bb72a-6aec-450a-9622-fe8f05f83d79>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#created>"),Soprano::Node::fromN3("\"2011-04-26T19:25:21.435Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/c07bb72a-6aec-450a-9622-fe8f05f83d79>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/01/19/nie#url>"),Soprano::Node::fromN3("<file:///home/vishesh/Videos/The%20Big%20Bang%20Theory/Season%201/7_Dumpling%20Paradox.avi>"),Soprano::Node::fromN3("<nepomuk:/ctx/33966b99-de71-4d77-8cbc-a3e3c104d681>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/8e251223-a066-4c90-863f-2f49237c870f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/8e251223-a066-4c90-863f-2f49237c870f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#Tag>"),Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/8e251223-a066-4c90-863f-2f49237c870f>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/8e251223-a066-4c90-863f-2f49237c870f>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#identifier>"),Soprano::Node::fromN3("\"Big%20Bang\"^^<http://www.w3.org/2001/XMLSchema#string>"),Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/8e251223-a066-4c90-863f-2f49237c870f>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#created>"),Soprano::Node::fromN3("\"2011-04-26T19:25:21.475Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Data>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Graph>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#InstanceBase>"),Soprano::Node::fromN3("<nepomuk:/ctx/a33dd431-cbf7-4aef-8217-a0dedfa7b56d>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#created>"),Soprano::Node::fromN3("\"2011-04-26T19:25:21.443Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/a33dd431-cbf7-4aef-8217-a0dedfa7b56d>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/1b99f767-3652-4bb5-97a5-dcb469ffa186>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/1b99f767-3652-4bb5-97a5-dcb469ffa186>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Data>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/1b99f767-3652-4bb5-97a5-dcb469ffa186>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Graph>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/1b99f767-3652-4bb5-97a5-dcb469ffa186>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#InstanceBase>"),Soprano::Node::fromN3("<nepomuk:/ctx/81107abc-443a-4d75-9cb3-3a93d18af6c2>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/1b99f767-3652-4bb5-97a5-dcb469ffa186>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/1b99f767-3652-4bb5-97a5-dcb469ffa186>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#created>"),Soprano::Node::fromN3("\"2011-05-24T10:35:39.52Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/81107abc-443a-4d75-9cb3-3a93d18af6c2>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/1b99f767-3652-4bb5-97a5-dcb469ffa186>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#maintainedBy>"),Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<nepomuk:/ctx/81107abc-443a-4d75-9cb3-3a93d18af6c2>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#Agent>"),Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#identifier>"),Soprano::Node::fromN3("\"nepomukindexer\"^^<http://www.w3.org/2001/XMLSchema#string>"),Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Data>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Graph>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#InstanceBase>"),Soprano::Node::fromN3("<nepomuk:/ctx/2e4f3918-7da3-4736-b998-2d1ab7cbd6e4>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#created>"),Soprano::Node::fromN3("\"2011-05-16T20:42:12.551Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/2e4f3918-7da3-4736-b998-2d1ab7cbd6e4>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Data>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Graph>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#InstanceBase>"),Soprano::Node::fromN3("<nepomuk:/ctx/a33dd431-cbf7-4aef-8217-a0dedfa7b56d>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/4da68d35-c6a2-4029-8fd9-a84ef7c2c60f>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#created>"),Soprano::Node::fromN3("\"2011-04-26T19:25:21.443Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/a33dd431-cbf7-4aef-8217-a0dedfa7b56d>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/33966b99-de71-4d77-8cbc-a3e3c104d681>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/33966b99-de71-4d77-8cbc-a3e3c104d681>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Data>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/33966b99-de71-4d77-8cbc-a3e3c104d681>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Graph>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/33966b99-de71-4d77-8cbc-a3e3c104d681>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#InstanceBase>"),Soprano::Node::fromN3("<nepomuk:/ctx/873b6b73-5dd5-44d3-b138-4b280065f5af>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/33966b99-de71-4d77-8cbc-a3e3c104d681>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/33966b99-de71-4d77-8cbc-a3e3c104d681>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#created>"),Soprano::Node::fromN3("\"2011-04-26T19:25:21.443Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/873b6b73-5dd5-44d3-b138-4b280065f5af>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/33966b99-de71-4d77-8cbc-a3e3c104d681>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#maintainedBy>"),Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<nepomuk:/ctx/873b6b73-5dd5-44d3-b138-4b280065f5af>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#Agent>"),Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#identifier>"),Soprano::Node::fromN3("\"nepomukindexer\"^^<http://www.w3.org/2001/XMLSchema#string>"),Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.w3.org/2000/01/rdf-schema#Resource>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Data>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#Graph>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nrl#InstanceBase>"),Soprano::Node::fromN3("<nepomuk:/ctx/2e4f3918-7da3-4736-b998-2d1ab7cbd6e4>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#userVisible>"),Soprano::Node::fromN3("\"1\"^^<http://www.w3.org/2001/XMLSchema#int>"),Soprano::Node::fromN3("<urn:crappyinference2:inferredtriples>") );
+    m_model->addStatement( Soprano::Node::fromN3("<nepomuk:/ctx/90105034-0d05-444a-8f7f-5a957dae9f14>"),Soprano::Node::fromN3("<http://www.semanticdesktop.org/ontologies/2007/08/15/nao#created>"),Soprano::Node::fromN3("\"2011-05-16T20:42:12.551Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>"),Soprano::Node::fromN3("<nepomuk:/ctx/2e4f3918-7da3-4736-b998-2d1ab7cbd6e4>") );
+
+
+    QUrl resUri("nepomuk:/res/c07bb72a-6aec-450a-9622-fe8f05f83d79");
+    QUrl nieUrl("file:///home/vishesh/Videos/The%20Big%20Bang%20Theory/Season%201/7_Dumpling%20Paradox.avi");
+
+    QString query = QString::fromLatin1("select ?app where { graph ?g { %1 %2 %3 .} "
+                                        "?g %4 ?app . ?app %5 %6 . }" )
+                    .arg( Node::resourceToN3( resUri ),
+                          Node::resourceToN3( NIE::url() ),
+                          Node::resourceToN3( nieUrl ),
+                          Node::resourceToN3( NAO::maintainedBy() ),
+                          Node::resourceToN3( NAO::identifier() ),
+                          Node::literalToN3( LiteralValue("nepomukindexer") ) );
+
+    QueryResultIterator it = m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
+    QUrl appUri;
+    if( it.next() ) {
+        appUri = it[0].uri();
+    }
+    QCOMPARE( appUri, QUrl("nepomuk:/res/e2eb2efb-14ee-4038-ac24-698f916289b0") );
+
+    m_dmModel->removeDataByApplication( QList<QUrl>() << resUri,
+                                        Nepomuk::RemoveSubResoures,
+                                        QLatin1String("nepomukindexer") );
+
+    QString query2 = QString::fromLatin1("ask where { graph ?g { ?r %1 %2 .} "
+                                        "?g %3 ?app . ?app %4 %5 . }" )
+                    .arg( Node::resourceToN3( NIE::url() ),
+                          Node::resourceToN3( nieUrl ),
+                          Node::resourceToN3( NAO::maintainedBy() ),
+                          Node::resourceToN3( NAO::identifier() ),
+                          Node::literalToN3( LiteralValue("nepomukindexer") ) );
+
+    QVERIFY( !m_model->executeQuery( query2, Soprano::Query::QueryLanguageSparql ).boolValue() );
+}
+
+void DataManagementModelTest::testRemoveDataByApplication_nieUrl()
+{
+    KTemporaryFile file;
+    file.open();
+    const QUrl fileUrl( file.fileName() );
+
+    const QUrl res1("nepomuk:/res/1");
+
+    // The file is tagged via Dolphin
+    const QUrl g1 = m_nrlModel->createGraph( NRL::InstanceBase() );
+    m_model->addStatement( res1, RDF::type(), NFO::FileDataObject() );
+    m_model->addStatement( res1, NIE::url(), fileUrl, g1 );
+    QDateTime now = QDateTime::currentDateTime();
+    m_model->addStatement( res1, NAO::created(), LiteralValue(QVariant(now)), g1 );
+    m_model->addStatement( res1, NAO::lastModified(), LiteralValue(QVariant(now)), g1 );
+
+    const QUrl tag("nepomuk:/res/tag");
+    m_model->addStatement( tag, RDF::type(), NAO::Tag(), g1 );
+    m_model->addStatement( tag, NAO::identifier(), LiteralValue("tag"), g1 );
+    m_model->addStatement( res1, NAO::hasTag(), tag, g1 );
+
+    // Indexed via strigi
+    SimpleResource simpleRes( res1 );
+    simpleRes.addProperty( RDF::type(), NFO::FileDataObject() );
+    simpleRes.addProperty( RDF::type(), NMM::MusicPiece() );
+    simpleRes.addProperty( NIE::url(), fileUrl );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << simpleRes, QLatin1String("nepomukindexer"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // Remove strigi indexed content
+    m_dmModel->removeDataByApplication( QList<QUrl>() << res1, Nepomuk::RemoveSubResoures,
+                                        QLatin1String("nepomukindexer") );
+
+    // The tag should still be there
+    QVERIFY( m_model->containsStatement( tag, RDF::type(), NAO::Tag(), g1 ) );
+    QVERIFY( m_model->containsStatement( tag, NAO::identifier(), LiteralValue("tag"), g1 ) );
+    QVERIFY( m_model->containsStatement( res1, NAO::hasTag(), tag, g1 ) );
+
+    // The resource should not have any data maintained by "nepomukindexer"
+    QString query = QString::fromLatin1("select * where { graph ?g { %1 ?p ?o. } ?g %2 ?a . ?a %3 %4. }")
+                    .arg( Node::resourceToN3( res1 ),
+                          Node::resourceToN3( NAO::maintainedBy() ),
+                          Node::resourceToN3( NAO::identifier() ),
+                          Node::literalToN3( LiteralValue("nepomukindexer") ) );
+
+    QList< BindingSet > bs = m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql ).allBindings();
+    kDebug() << bs;
+    QVERIFY( bs.isEmpty() );
+
+    // The nie:url should still exist
+    QVERIFY( m_model->containsAnyStatement( res1, NIE::url(), fileUrl ) );
+}
+
+// make sure we keep the nie:url in case a relation from another resource exists which is not removed
+void DataManagementModelTest::testRemoveDataByApplication_nieUrlRelated()
+{
+    // create the file we will be removing data for
+    KTemporaryFile file;
+    file.open();
+    const KUrl fileUrl( file.fileName() );
+
+
+    // Indexed the file with some data
+    SimpleResource simpleRes( fileUrl );
+    simpleRes.addProperty( RDF::type(), NFO::FileDataObject() );
+    simpleRes.addProperty( RDF::type(), NMM::MusicPiece() );
+    m_dmModel->storeResources( SimpleResourceGraph() << simpleRes, QLatin1String("A"));
+    QVERIFY( !m_dmModel->lastError() );
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), NIE::url(), fileUrl));
+
+
+    // now add a relation to some other resource
+    const QUrl res = m_dmModel->createResource(QList<QUrl>() << QUrl("class:/typeA"), QLatin1String("foobar"), QString(), QLatin1String("B"));
+    QVERIFY( !m_dmModel->lastError() );
+    m_dmModel->addProperty(QList<QUrl>() << res, QUrl("prop:/res"), QVariantList() << QUrl(fileUrl), QLatin1String("B"));
+    QVERIFY( !m_dmModel->lastError() );
+
+
+    // remove all the indexed data
+    m_dmModel->removeDataByApplication(QList<QUrl>() << fileUrl, NoRemovalFlags, QLatin1String("A"));
+    QVERIFY( !m_dmModel->lastError() );
+
+
+    // now the basic data from the indexed file should still be there
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), NIE::url(), fileUrl));
+
+    // and the relation created by the other application should still be there, too
+    const QUrl fileResUri = m_model->listStatements(Soprano::Node(), NIE::url(), fileUrl).allElements().first().subject().uri();
+    QVERIFY(m_model->containsAnyStatement(res, QUrl("prop:/res"), fileResUri));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// make sure the mtime is updated properly in different situations
+void DataManagementModelTest::testRemoveDataByApplication_mtime()
+{
+    // first we create our apps and graphs
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    const QDateTime date = QDateTime::currentDateTime();
+
+
+    // now we create different resources
+    // A has actual data maintained by app:/A
+    // B has only metadata maintained by app:/A
+    // C has nothing maintained by app:/A
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello")), g2);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::lastModified(), LiteralValue(date), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+    m_model->addStatement(QUrl("res:/B"), NAO::created(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/B"), NAO::lastModified(), LiteralValue(date), g1);
+
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+    m_model->addStatement(QUrl("res:/C"), NAO::created(), LiteralValue(date), g2);
+    m_model->addStatement(QUrl("res:/C"), NAO::lastModified(), LiteralValue(date), g2);
+
+
+    // we delete all three
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A") << QUrl("res:/B") << QUrl("res:/C"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+
+    // now only the mtime of A should have changed
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), NAO::lastModified(), Node()).allElements().count(), 1);
+    QVERIFY(m_model->listStatements(QUrl("res:/A"), NAO::lastModified(), Node()).allElements().first().object().literal().toDateTime() > date);
+
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), NAO::lastModified(), Node()).allElements().count(), 1);
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), NAO::lastModified(), Node()).allElements().first().object().literal().toDateTime(), date);
+
+    QCOMPARE(m_model->listStatements(QUrl("res:/C"), NAO::lastModified(), Node()).allElements().count(), 1);
+    QCOMPARE(m_model->listStatements(QUrl("res:/C"), NAO::lastModified(), Node()).allElements().first().object().literal().toDateTime(), date);
+}
+
+// make sure the mtime of resources that are related to deleted ones is updated
+void DataManagementModelTest::testRemoveDataByApplication_mtimeRelated()
+{
+    // first we create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    const QDateTime date = QDateTime::currentDateTime();
+
+    // we three two resources - one to delete, and one which is related to the one to be deleted,
+    // one which is also related but will not be changed
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello")), g2);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/res"), QUrl("res:/A"), g1);
+    m_model->addStatement(QUrl("res:/B"), NAO::created(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/B"), NAO::lastModified(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/res"), QUrl("res:/A"), g2);
+    m_model->addStatement(QUrl("res:/C"), NAO::created(), LiteralValue(date), g2);
+    m_model->addStatement(QUrl("res:/C"), NAO::lastModified(), LiteralValue(date), g2);
+
+    // now we remove res:/A
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // now the mtime of res:/B should have been changed
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), NAO::lastModified(), Node()).allElements().count(), 1);
+    QVERIFY(m_model->listStatements(QUrl("res:/B"), NAO::lastModified(), Node()).allElements().first().object().literal().toDateTime() > date);
+
+    // the mtime of res:/C should NOT have changed
+    QCOMPARE(m_model->listStatements(QUrl("res:/C"), NAO::lastModified(), Node()).allElements().count(), 1);
+    QCOMPARE(m_model->listStatements(QUrl("res:/C"), NAO::lastModified(), Node()).allElements().first().object().literal().toDateTime(), date);
+}
+
+// make sure relations to removed resources are handled properly
+void DataManagementModelTest::testRemoveDataByApplication_related()
+{
+    // first we create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    // we create 3 graphs, maintained by different apps
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+    QUrl mg3;
+    const QUrl g3 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg3);
+    m_model->addStatement(g3, NAO::maintainedBy(), QUrl("app:/A"), mg3);
+
+    const QDateTime date = QDateTime::currentDateTime();
+
+    // create two resources:
+    // A is split across both graphs
+    // B is only in one graph
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("Hello World")), g2);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::lastModified(), LiteralValue(date), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/B"), NAO::created(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/B"), NAO::lastModified(), LiteralValue(date), g1);
+
+    // three relations B -> A, one in each graph
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/res"), QUrl("res:/A"), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/res2"), QUrl("res:/A"), g2);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/res3"), QUrl("res:/A"), g3);
+
+    // a third resource which is deleted entirely but has one relation in another graph
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/C"), NAO::created(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/C"), NAO::lastModified(), LiteralValue(date), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/res"), QUrl("res:/C"), g2);
+
+
+    // now remove A
+    m_dmModel->removeDataByApplication(QList<QUrl>() << QUrl("res:/A") << QUrl("res:/C"), Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // now only the relation in the first graph should have been removed
+    QVERIFY(!m_model->containsStatement(QUrl("res:/B"), QUrl("prop:/res"), QUrl("res:/A"), g1));
+    QVERIFY(!m_model->containsStatement(QUrl("res:/B"), QUrl("prop:/res3"), QUrl("res:/A"), g3));
+    QVERIFY(m_model->containsStatement(QUrl("res:/B"), QUrl("prop:/res2"), QUrl("res:/A"), g2));
+    QVERIFY(!m_model->containsStatement(QUrl("res:/B"), QUrl("prop:/res3"), QUrl("res:/C"), g2));
+
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), QUrl("prop:/res"), QUrl("res:/A")));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), QUrl("prop:/res3"), QUrl("res:/A")));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/B"), QUrl("prop:/res2"), QUrl("res:/A")));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), QUrl("prop:/res3"), QUrl("res:/C")));
+}
+
+// make sure legacy indexer data (the graphs marked with indexGraphFor) is removed properly
+void DataManagementModelTest::testRemoveDataByApplication_legacyIndexerData()
+{
+    // create our file
+    QTemporaryFile fileA;
+    fileA.open();
+    const QUrl fileAUrl = QUrl::fromLocalFile(fileA.fileName());
+    const QUrl fileARes("res:/A");
+
+    // create the graph containing the legacy data
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::DiscardableInstanceBase(), &mg1);
+
+    // mark the graph as being the legacy index graph
+    m_model->addStatement(g1, QUrl("http://www.strigi.org/fields#indexGraphFor"), fileARes, mg1);
+
+    // create the index data
+    m_model->addStatement(fileARes, NIE::url(), fileAUrl, g1);
+    m_model->addStatement(fileARes, RDF::type(), NFO::FileDataObject(), g1);
+    m_model->addStatement(fileARes, RDF::type(), NIE::InformationElement(), g1);
+    m_model->addStatement(fileARes, NIE::title(), LiteralValue(QLatin1String("foobar")), g1);
+
+
+    // remove the information claiming to be the indexer
+    QBENCHMARK_ONCE
+    m_dmModel->removeDataByApplication(QList<QUrl>() << fileAUrl, NoRemovalFlags, QLatin1String("nepomukindexer"));
+
+    // the call should succeed
+    QVERIFY(!m_dmModel->lastError());
+
+    // now make sure that everything is gone
+    QVERIFY(!m_model->containsAnyStatement(fileARes, Node(), Node(), Node()));
+
+    QVERIFY(!m_model->containsAnyStatement(Node(), Node(), Node(), g1));
+    QVERIFY(!m_model->containsAnyStatement(Node(), Node(), Node(), mg1));
+}
+
+// make sure we can remove data from non-existing files
+void DataManagementModelTest::testRemoveDataByApplication_deletedFile()
+{
+    QTemporaryFile* fileA = new QTemporaryFile();
+    fileA->open();
+    const KUrl fileUrl(fileA->fileName());
+    delete fileA;
+    QVERIFY(!QFile::exists(fileUrl.toLocalFile()));
+
+    // create our app
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create the data graph
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    // create the resource
+    m_model->addStatement(QUrl("res:/A"), NIE::url(), fileUrl, g1);
+    m_model->addStatement(QUrl("res:/A"), RDF::type(), NFO::FileDataObject(), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+
+    // now try removing the data
+    m_dmModel->removeDataByApplication(QList<QUrl>() << fileUrl, NoRemovalFlags, QLatin1String("A"));
+
+    // the call should succeed
+    QVERIFY(!m_dmModel->lastError());
+
+    // the resource should be gone
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(Node(), Node(), fileUrl));
+}
+
+// test that all is removed, ie. storage is clear afterwards
+void DataManagementModelTest::testRemoveAllDataByApplication1()
+{
+    // create our app
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/A"), mg2);
+
+    // create two resources to remove
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar 2")), g2);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world 2")), g2);
+    m_model->addStatement(QUrl("res:/B"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    m_dmModel->removeDataByApplication(Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    // make sure nothing is there anymore
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/B"), Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(Node(), NAO::maintainedBy(), QUrl("app:/A")));
+
+    QVERIFY(!haveTrailingGraphs());
+
+    // everything should be as before
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+// test that other resources are not removed - the easy way
+void DataManagementModelTest::testRemoveAllDataByApplication2()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    // create two resources to remove
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar 2")), g2);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world 2")), g2);
+    m_model->addStatement(QUrl("res:/B"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g2);
+
+    m_dmModel->removeDataByApplication(Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    QVERIFY(!m_model->containsAnyStatement(QUrl("res:/A"), Node(), Node()));
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), Node(), Node()).allStatements().count(), 3);
+    QVERIFY(!m_model->containsAnyStatement(Node(), NAO::maintainedBy(), QUrl("app:/A")));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// test that an app is simply removed as maintainer of a graph
+void DataManagementModelTest::testRemoveAllDataByApplication3()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/B"), mg1);
+
+    // create two resources to remove
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar 2")), g1);
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world 2")), g1);
+    m_model->addStatement(QUrl("res:/B"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    m_dmModel->removeDataByApplication(Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), Node(), Node()).allStatements().count(), 3);
+    QCOMPARE(m_model->listStatements(QUrl("res:/B"), Node(), Node()).allStatements().count(), 3);
+    QVERIFY(!m_model->containsAnyStatement(Node(), NAO::maintainedBy(), QUrl("app:/A")));
+    QCOMPARE(m_model->listStatements(Node(), NAO::maintainedBy(), QUrl("app:/B")).allStatements().count(), 1);
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+// test that metadata is not removed if the resource still exists even if its in a deleted graph
+void DataManagementModelTest::testRemoveAllDataByApplication4()
+{
+    // create our apps
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/B"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/B"), NAO::identifier(), LiteralValue(QLatin1String("B")), appG);
+
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+    QUrl mg2;
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg2);
+    m_model->addStatement(g2, NAO::maintainedBy(), QUrl("app:/B"), mg2);
+
+    // create two resources to remove
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g2);
+    m_model->addStatement(QUrl("res:/A"), NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    m_dmModel->removeDataByApplication(Nepomuk::NoRemovalFlags, QLatin1String("A"));
+
+    QCOMPARE(m_model->listStatements(QUrl("res:/A"), Node(), Node()).allStatements().count(), 3);
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NAO::created(), Soprano::Node()));
+    QVERIFY(m_model->containsAnyStatement(QUrl("res:/A"), NAO::lastModified(), Soprano::Node()));
+    QVERIFY(m_model->containsAnyStatement(Node(), NAO::maintainedBy(), QUrl("app:/A")));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+
+namespace {
+    int push( Soprano::Model * model, Nepomuk::SimpleResource res, QUrl graph ) {
+        QHashIterator<QUrl, QVariant> it( res.properties() );
+        if( !res.uri().isValid() )
+            return 0;
+
+        int numPushed = 0;
+        Soprano::Statement st( res.uri(), Soprano::Node(), Soprano::Node(), graph );
+        while( it.hasNext() ) {
+            it.next();
+            st.setPredicate( it.key() );
+            if(it.value().type() == QVariant::Url)
+                st.setObject(it.value().toUrl());
+            else
+                st.setObject( Soprano::LiteralValue(it.value()) );
+            if( model->addStatement( st ) == Soprano::Error::ErrorNone )
+                numPushed++;
+        }
+        return numPushed;
+    }
+}
+
+
+void DataManagementModelTest::testStoreResources_strigiCase()
+{
+    //
+    // This is for testing exactly how Strigi will use storeResources ie
+    // have some blank nodes ( some of which may already exists ) and a
+    // main resources which does not exist
+    //
+
+    kDebug() << "Starting Strigi merge test";
+
+    QUrl graphUri = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    Nepomuk::SimpleResource coldplay;
+    coldplay.addProperty( RDF::type(), NCO::Contact() );
+    coldplay.addProperty( NCO::fullname(), "Coldplay" );
+
+    // Push it into the model with a proper uri
+    coldplay.setUri( QUrl("nepomuk:/res/coldplay") );
+    QVERIFY( push( m_model, coldplay, graphUri ) == coldplay.properties().size() );
+
+    // Now keep it as a blank node
+    coldplay.setUri( QUrl("_:coldplay") );
+
+    Nepomuk::SimpleResource album;
+    album.setUri( QUrl("_:XandY") );
+    album.addProperty( RDF::type(), NMM::MusicAlbum() );
+    album.addProperty( NIE::title(), "X&Y" );
+
+    Nepomuk::SimpleResource res1;
+    res1.addProperty( RDF::type(), NFO::FileDataObject() );
+    res1.addProperty( RDF::type(), NMM::MusicPiece() );
+    res1.addProperty( NFO::fileName(), "Yellow.mp3" );
+    res1.addProperty( NMM::performer(), QUrl("_:coldplay") );
+    res1.addProperty( NMM::musicAlbum(), QUrl("_:XandY") );
+    Nepomuk::SimpleResourceGraph resGraph;
+    resGraph << res1 << coldplay << album;
+
+    //
+    // Do the actual merging
+    //
+    kDebug() << "Perform the merge";
+    m_dmModel->storeResources( resGraph, "TestApp" );
+    QVERIFY( !m_dmModel->lastError() );
+
+    QList<Soprano::Statement> stList = m_model->listStatements( Node(), RDF::type(), NMM::MusicPiece() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl res1Uri = stList.first().subject().uri();
+
+    QVERIFY( m_model->containsAnyStatement( res1Uri, Soprano::Node(),
+                                            Soprano::Node() ) );
+    QVERIFY( m_model->containsAnyStatement( res1Uri, NFO::fileName(),
+                                            Soprano::LiteralValue("Yellow.mp3") ) );
+    // Make sure we have the nao:created and nao:lastModified
+    QVERIFY( m_model->containsAnyStatement( res1Uri, NAO::lastModified(),
+                                            Soprano::Node() ) );
+    QVERIFY( m_model->containsAnyStatement( res1Uri, NAO::created(),
+                                            Soprano::Node() ) );
+    kDebug() << m_model->listStatements( res1Uri, Soprano::Node(), Soprano::Node() ).allStatements();
+    // The +2 is because nao:created and nao:lastModified would have also been added
+    QCOMPARE( m_model->listStatements( res1Uri, Soprano::Node(), Soprano::Node() ).allStatements().size(),
+                res1.properties().size() + 2 );
+
+    QList< Node > objects = m_model->listStatements( res1Uri, NMM::performer(), Soprano::Node() ).iterateObjects().allNodes();
+
+    QVERIFY( objects.size() == 1 );
+    QVERIFY( objects.first().isResource() );
+
+    QUrl coldplayUri = objects.first().uri();
+    QCOMPARE( coldplayUri, QUrl("nepomuk:/res/coldplay") );
+    stList = coldplay.toStatementList();
+    foreach( Soprano::Statement st, stList ) {
+        st.setSubject( coldplayUri );
+        QVERIFY( m_model->containsAnyStatement( st ) );
+    }
+
+    objects = m_model->listStatements( res1Uri, NMM::musicAlbum(), Soprano::Node() ).iterateObjects().allNodes();
+
+    QVERIFY( objects.size() == 1 );
+    QVERIFY( objects.first().isResource() );
+
+    QUrl albumUri = objects.first().uri();
+    stList = album.toStatementList();
+    foreach( Soprano::Statement st, stList ) {
+        st.setSubject( albumUri );
+        QVERIFY( m_model->containsAnyStatement( st ) );
+    }
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+
+void DataManagementModelTest::testStoreResources_graphRules()
+{
+    //
+    // Test the graph rules. If a resource exists in a nrl:DiscardableInstanceBase
+    // and it is merged with a non-discardable graph. Then the graph should be replaced
+    // In the opposite case - nothing should be done
+    {
+        Nepomuk::SimpleResource res;
+        res.addProperty( RDF::type(), NCO::Contact() );
+        res.addProperty( NCO::fullname(), "Lion" );
+
+        QUrl graphUri = m_nrlModel->createGraph( NRL::DiscardableInstanceBase() );
+
+        res.setUri(QUrl("nepomuk:/res/Lion"));
+        QVERIFY( push( m_model, res, graphUri ) == res.properties().size() );
+        res.setUri(QUrl("_:lion"));
+
+        Nepomuk::SimpleResourceGraph resGraph;
+        resGraph << res;
+
+        QHash<QUrl, QVariant> additionalMetadata;
+        additionalMetadata.insert( RDF::type(), NRL::InstanceBase() );
+        m_dmModel->storeResources( resGraph, "TestApp", Nepomuk::IdentifyNew, Nepomuk::NoStoreResourcesFlags, additionalMetadata );
+        QVERIFY( !m_dmModel->lastError() );
+
+        QList<Soprano::Statement> stList = m_model->listStatements( Soprano::Node(), NCO::fullname(),
+                                                            Soprano::LiteralValue("Lion") ).allStatements();
+        kDebug() << stList;
+        QVERIFY( stList.size() == 1 );
+        Soprano::Node lionNode = stList.first().subject();
+        QVERIFY( lionNode.isResource() );
+        Soprano::Node graphNode = stList.first().context();
+        QVERIFY( graphNode.isResource() );
+
+        QVERIFY( !m_model->containsAnyStatement( graphNode, RDF::type(), NRL::DiscardableInstanceBase() ) );
+
+        QVERIFY(!haveTrailingGraphs());
+    }
+    {
+        Nepomuk::SimpleResource res;
+        res.addProperty( RDF::type(), NCO::Contact() );
+        res.addProperty( NCO::fullname(), "Tiger" );
+
+        QUrl graphUri = m_nrlModel->createGraph( NRL::DiscardableInstanceBase() );
+
+        res.setUri(QUrl("nepomuk:/res/Tiger"));
+        QVERIFY( push( m_model, res, graphUri ) == res.properties().size() );
+        res.setUri(QUrl("_:tiger"));
+
+        Nepomuk::SimpleResourceGraph resGraph;
+        resGraph << res;
+
+        QHash<QUrl, QVariant> additionalMetadata;
+        additionalMetadata.insert( RDF::type(), NRL::InstanceBase() );
+        m_dmModel->storeResources( resGraph, "TestApp", Nepomuk::IdentifyNew, Nepomuk::NoStoreResourcesFlags, additionalMetadata );
+        QVERIFY( !m_dmModel->lastError() );
+
+        QList<Soprano::Statement> stList = m_model->listStatements( Soprano::Node(), NCO::fullname(),
+                                                           Soprano::LiteralValue("Tiger") ).allStatements();
+        QVERIFY( stList.size() == 1 );
+        Soprano::Node TigerNode = stList.first().subject();
+        QVERIFY( TigerNode.isResource() );
+        Soprano::Node graphNode = stList.first().context();
+        QVERIFY( graphNode.isResource() );
+
+        QVERIFY( !m_model->containsAnyStatement( graphNode, RDF::type(), NRL::DiscardableInstanceBase() ) );
+
+        QVERIFY(!haveTrailingGraphs());
+    }
+}
+
+
+void DataManagementModelTest::testStoreResources_createResource()
+{
+    //
+    // Simple case: create a resource by merging it
+    //
+    SimpleResource res;
+    res.setUri(QUrl("_:A"));
+    res.addProperty(RDF::type(), NAO::Tag());
+    res.addProperty(NAO::prefLabel(), QLatin1String("Foobar"));
+
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("testapp"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // check if the resource exists
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), RDF::type(), NAO::Tag()));
+    QVERIFY(m_model->containsAnyStatement(Soprano::Node(), NAO::prefLabel(), Soprano::LiteralValue::createPlainLiteral(QLatin1String("Foobar"))));
+
+    // make sure only one tag resource was created
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NAO::Tag()).allElements().count(), 1);
+
+    // get the new resources URI
+    const QUrl resUri = m_model->listStatements(Node(), RDF::type(), NAO::Tag()).iterateSubjects().allNodes().first().uri();
+
+    // check that it has the default metadata
+    QVERIFY(m_model->containsAnyStatement(resUri, NAO::created(), Node()));
+    QVERIFY(m_model->containsAnyStatement(resUri, NAO::lastModified(), Node()));
+
+    // and both created and last modification date should be similar
+    QCOMPARE(m_model->listStatements(resUri, NAO::created(), Node()).iterateObjects().allNodes().first(),
+             m_model->listStatements(resUri, NAO::lastModified(), Node()).iterateObjects().allNodes().first());
+
+    // check if all the correct metadata graphs exist
+    // ask where {
+    //  graph ?g { ?r a nao:Tag . ?r nao:prefLabel "Foobar" . } .
+    //  graph ?mg { ?g a nrl:InstanceBase . ?mg a nrl:GraphMetadata . ?mg nrl:coreGraphMetadataFor ?g . } .
+    //  ?g nao:maintainedBy ?a . ?a nao:identifier "testapp"
+    // }
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { ?r a %1 . ?r %2 %3 . } . "
+                                                      "graph ?mg { ?g a %4 . ?mg a %5 . ?mg %6 ?g . } . "
+                                                      "?g %7 ?a . ?a %8 %9 . "
+                                                      "}")
+                                  .arg(Soprano::Node::resourceToN3(NAO::Tag()),
+                                       Soprano::Node::resourceToN3(NAO::prefLabel()),
+                                       Soprano::Node::literalToN3(LiteralValue::createPlainLiteral(QLatin1String("Foobar"))),
+                                       Soprano::Node::resourceToN3(NRL::InstanceBase()),
+                                       Soprano::Node::resourceToN3(NRL::GraphMetadata()),
+                                       Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()),
+                                       Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                       Soprano::Node::resourceToN3(NAO::identifier()),
+                                       Soprano::Node::literalToN3(QLatin1String("testapp"))),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    //
+    // Now create the same resource again
+    //
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("testapp"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // nothing should have happened
+    QCOMPARE(existingStatements, Soprano::Graph(m_model->listStatements().allStatements()));
+
+    //
+    // Now create the same resource with a different app
+    //
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("testapp2"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // only one thing should have been added: the new app Agent and its role as maintainer for the existing graph
+    // vHanda: Shouldn't there be a new graph, with the resources statements which hash both
+    //         testapp and testapp2 as maintainers?
+
+    //Q_FOREACH(const Soprano::Statement& s, existingStatements.toList()) {
+    //    kDebug() << s;
+    //    QVERIFY(m_model->containsStatement(s));
+    //}
+
+    // ask where {
+    //      graph ?g { ?r a nao:Tag. ?r nao:prefLabel "Foobar" . } .
+    //      ?g nao:maintainedBy ?a1 . ?a1 nao:identifier "testapp" .
+    //      ?g nao:maintainedBy ?a2 . ?a2 nao:identifier "testapp2" .
+    // }
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                      "graph ?g { ?r a %1 . ?r %2 %3 . } . "
+                                                      "?g %4 ?a1 . ?a1 %5 %6 . "
+                                                      "?g %4 ?a2 . ?a2 %5 %7 . "
+                                                      "}")
+                                  .arg(Soprano::Node::resourceToN3(NAO::Tag()),
+                                       Soprano::Node::resourceToN3(NAO::prefLabel()),
+                                       Soprano::Node::literalToN3(LiteralValue::createPlainLiteral(QLatin1String("Foobar"))),
+                                       Soprano::Node::resourceToN3(NAO::maintainedBy()),
+                                       Soprano::Node::resourceToN3(NAO::identifier()),
+                                       Soprano::Node::literalToN3(QLatin1String("testapp")),
+                                       Soprano::Node::literalToN3(QLatin1String("testapp2"))),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+
+    // create a resource by specifying the URI
+//     SimpleResource res2;
+//     res2.setUri(QUrl("nepomuk:/res/A"));
+//     res2.addProperty(QUrl("prop:/string"), QVariant(QLatin1String("foobar")));
+//     m_dmModel->storeResources(SimpleResourceGraph() << res2, QLatin1String("testapp"));
+//     QVERIFY( !m_dmModel->lastError() );
+//
+//     QVERIFY(m_model->containsAnyStatement( res2.uri(), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testStoreResources_invalid_args()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // empty resources -> no error but no change either
+    m_dmModel->storeResources(SimpleResourceGraph(), QLatin1String("testapp"));
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty app
+    m_dmModel->storeResources(SimpleResourceGraph(), QString());
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid resource in graph
+    m_dmModel->storeResources(SimpleResourceGraph() << SimpleResource(), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid range used in one resource
+    SimpleResource res;
+    res.setUri(QUrl("nepomuk:/res/A"));
+    res.addProperty(QUrl("prop:/int"), QVariant(QLatin1String("foobar")));
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // non-existing file
+    const QUrl nonExistingFileUrl("file:///a/file/that/is/very/unlikely/to/exist");
+    SimpleResource nonExistingFileRes;
+    nonExistingFileRes.setUri(nonExistingFileUrl);
+    nonExistingFileRes.addProperty(QUrl("prop:/int"), QVariant(42));
+    m_dmModel->storeResources(SimpleResourceGraph() << nonExistingFileRes, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // non-existing file as object
+    nonExistingFileRes.setUri(QUrl("nepomuk:/res/A"));
+    nonExistingFileRes.addProperty(QUrl("prop:/res"), nonExistingFileUrl);
+    m_dmModel->storeResources(SimpleResourceGraph() << nonExistingFileRes, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid graph metadata 1
+    SimpleResource res2;
+    res.setUri(QUrl("nepomuk:/res/A"));
+    res.addProperty(QUrl("prop:/int"), QVariant(42));
+    QHash<QUrl, QVariant> invalidMetadata;
+    invalidMetadata.insert(QUrl("prop:/int"), QLatin1String("foobar"));
+    m_dmModel->storeResources(SimpleResourceGraph() << res2, QLatin1String("testapp"), Nepomuk::IdentifyNew, Nepomuk::NoStoreResourcesFlags, invalidMetadata);
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid graph metadata 2
+    invalidMetadata.clear();
+    invalidMetadata.insert(NAO::maintainedBy(), QLatin1String("foobar"));
+    m_dmModel->storeResources(SimpleResourceGraph() << res2, QLatin1String("testapp"), Nepomuk::IdentifyNew, Nepomuk::NoStoreResourcesFlags, invalidMetadata);
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+void DataManagementModelTest::testStoreResources_invalid_args_with_existing()
+{
+    // create a test resource
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    // create the resource to delete
+    QUrl resA("nepomuk:/res/A");
+    QUrl resB("nepomuk:/res/B");
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(resA, QUrl("prop:/res"), resB, g1);
+    const QDateTime now = QDateTime::currentDateTime();
+    m_model->addStatement(resA, NAO::created(), LiteralValue(now), g1);
+    m_model->addStatement(resA, NAO::lastModified(), LiteralValue(now), g1);
+
+
+    // create a resource to merge
+    SimpleResource a(resA);
+    a.addProperty(QUrl("prop:/int"), 42);
+
+
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // empty resources -> no error but no change either
+    m_dmModel->storeResources(SimpleResourceGraph(), QLatin1String("testapp"));
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // empty app
+    m_dmModel->storeResources(SimpleResourceGraph() << a, QString());
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid resource in graph
+    m_dmModel->storeResources(SimpleResourceGraph() << a << SimpleResource(), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid range used in one resource
+    SimpleResource res(a);
+    res.addProperty(QUrl("prop:/int"), QVariant(QLatin1String("foobar")));
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // non-existing file as object
+    const QUrl nonExistingFileUrl("file:///a/file/that/is/very/unlikely/to/exist");
+    SimpleResource nonExistingFileRes(a);
+    nonExistingFileRes.addProperty(QUrl("prop:/res"), nonExistingFileUrl);
+    m_dmModel->storeResources(SimpleResourceGraph() << nonExistingFileRes, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // invalid cardinality in resource
+    SimpleResource invalidCRes(a);
+    invalidCRes.addProperty(QUrl("prop:/int_c1"), 42);
+    invalidCRes.addProperty(QUrl("prop:/int_c1"), 2);
+    m_dmModel->storeResources(SimpleResourceGraph() << invalidCRes, QLatin1String("testapp"));
+
+    // the call should have failed
+    QVERIFY(m_dmModel->lastError());
+
+    // nothing should have changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+void DataManagementModelTest::testStoreResources_file1()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+
+    // merge a file URL
+    SimpleResource r1;
+    r1.setUri(QUrl::fromLocalFile(fileA.fileName()));
+    r1.addProperty(RDF::type(), NAO::Tag());
+    r1.addProperty(QUrl("prop:/string"), QLatin1String("Foobar"));
+
+    m_dmModel->storeResources(SimpleResourceGraph() << r1, QLatin1String("testapp"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // a nie:url relation should have been created
+    QVERIFY(m_model->containsAnyStatement(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())));
+
+    // the file URL should never be used as subject
+    QVERIFY(!m_model->containsAnyStatement(QUrl::fromLocalFile(fileA.fileName()), Node(), Node()));
+
+    // make sure file URL and res URI are properly related including the properties
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { ?r %1 %4 . "
+                                                      "?r a %2 . "
+                                                      "?r <prop:/string> %3 . }")
+                                  .arg(Node::resourceToN3(NIE::url()),
+                                       Node::resourceToN3(NAO::Tag()),
+                                       Node::literalToN3(LiteralValue(QLatin1String("Foobar"))),
+                                       Node::resourceToN3(QUrl::fromLocalFile(fileA.fileName()))),
+                                  Query::QueryLanguageSparql).boolValue());
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testStoreResources_file2()
+{
+    // merge a property with non-existing file value
+    QTemporaryFile fileA;
+    fileA.open();
+    const QUrl fileUrl = QUrl::fromLocalFile(fileA.fileName());
+
+    SimpleResource r1;
+    r1.addProperty(QUrl("prop:/res"), fileUrl);
+
+    m_dmModel->storeResources(SimpleResourceGraph() << r1, QLatin1String("testapp"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    QList<Soprano::Statement> stList = m_model->listStatements( Node(), QUrl("prop:/res"), Node() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl r1Uri = stList.first().subject().uri();
+
+    // the property should have been created
+    QVERIFY(m_model->containsAnyStatement(r1Uri, QUrl("prop:/res"), Node()));
+
+    // but it should not be related to the file URL
+    QVERIFY(!m_model->containsAnyStatement(r1Uri, QUrl("prop:/res"), fileUrl));
+
+    // there should be a nie:url for the file URL
+    QVERIFY(m_model->containsAnyStatement(Node(), NIE::url(), fileUrl));
+
+    // make sure file URL and res URI are properly related including the properties
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { %3 <prop:/res> ?r . "
+                                                      "?r %1 %2 . }")
+                                  .arg(Node::resourceToN3(NIE::url()),
+                                       Node::resourceToN3(fileUrl),
+                                       Node::resourceToN3(r1Uri)),
+                                  Query::QueryLanguageSparql).boolValue());
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+
+void DataManagementModelTest::testStoreResources_file3()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+    const QUrl fileUrl = QUrl::fromLocalFile(fileA.fileName());
+
+    SimpleResource r1;
+    r1.setUri( fileUrl );
+    r1.addProperty( RDF::type(), QUrl("class:/typeA") );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << r1, QLatin1String("origApp") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    // Make sure all the data has been added and it belongs to origApp
+    QString query = QString::fromLatin1("ask { graph ?g { ?r a %6 . "
+                                        " ?r %7 %1 . "
+                                        " ?r a %2 . } ?g %3 ?app . ?app %4 %5 . }")
+                    .arg( Soprano::Node::resourceToN3( fileUrl ),
+                          Soprano::Node::resourceToN3( QUrl("class:/typeA") ),
+                          Soprano::Node::resourceToN3( NAO::maintainedBy() ),
+                          Soprano::Node::resourceToN3( NAO::identifier() ),
+                          Soprano::Node(Soprano::LiteralValue("origApp")).toN3(),
+                          Soprano::Node::resourceToN3( NFO::FileDataObject()),
+                          Soprano::Node::resourceToN3( NIE::url() )
+                        );
+
+    QVERIFY( m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue() );
+
+    QList< Statement > stList = m_model->listStatements( Soprano::Node(), NIE::url(), fileUrl ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    QUrl resUri = stList.first().subject().uri();
+    QVERIFY( resUri.scheme() == QLatin1String("nepomuk") );
+
+    SimpleResource r2;
+    r2.setUri( fileUrl );
+    r2.addProperty( QUrl("prop:/res"), NFO::FileDataObject() );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << r2, QLatin1String("newApp") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    // Make sure it was identified properly
+    stList = m_model->listStatements( Soprano::Node(), NIE::url(), fileUrl ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    QUrl resUri2 = stList.first().subject().uri();
+    QVERIFY( resUri2.scheme() == QLatin1String("nepomuk") );
+    QCOMPARE( resUri, resUri2 );
+
+    // The only statement that should have acquired "newApp" as the maintainer should be
+    // r2 <prop:/res> <object:/custom>
+
+    query = QString::fromLatin1("select ?name where { graph ?g { %1 %2 %3 . %1 a %4. }"
+                                " ?g %5 ?app . ?app %6 ?name . }")
+                .arg( Soprano::Node::resourceToN3( resUri ),
+                      Soprano::Node::resourceToN3( NIE::url() ),
+                      Soprano::Node::resourceToN3( fileUrl ),
+                      Soprano::Node::resourceToN3( NFO::FileDataObject() ),
+                      Soprano::Node::resourceToN3( NAO::maintainedBy() ),
+                      Soprano::Node::resourceToN3( NAO::identifier() ) );
+
+    QueryResultIterator it = m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
+    QStringList appList;
+    while( it.next() )
+        appList << it["name"].literal().toString();
+
+    QCOMPARE( appList.size(), 1 );
+
+    //query = QString::fromLatin1("ask { )
+
+}
+
+
+void DataManagementModelTest::testStoreResources_file4()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+    const QUrl fileUrl = QUrl::fromLocalFile(fileA.fileName());
+
+    SimpleResource res;
+    res.addProperty( RDF::type(), fileUrl );
+    res.addProperty( QUrl("prop:/res"), fileUrl );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("app1") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    // Make sure the fileUrl got added
+    QList< Statement > stList = m_model->listStatements( Node(), NIE::url(), fileUrl ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    QUrl fileResUri = stList.first().subject().uri();
+
+    // Make sure the SimpleResource was stored
+    stList = m_model->listStatements( Node(), RDF::type(), fileResUri ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    QUrl resUri = stList.first().subject().uri();
+
+    // Check for the other statement
+    stList = m_model->listStatements( resUri, QUrl("prop:/res"), Node() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    QUrl fileResUri2 = stList.first().object().uri();
+    QCOMPARE( fileResUri, fileResUri2 );
+}
+
+
+void DataManagementModelTest::testStoreResources_folder()
+{
+    QTemporaryFile file;
+    QVERIFY( file.open() );
+    KUrl fileUrl = KUrl::fromLocalFile( file.fileName() );
+    QUrl folderUrl = QUrl::fromLocalFile( fileUrl.directory() );
+
+    SimpleResource res( fileUrl );
+    res.addProperty( RDF::type(), NMM::MusicPiece() );
+    res.addProperty( NAO::prefLabel(), QLatin1String("Label") );
+    res.addProperty( NIE::isPartOf(), fileUrl.directory() );
+
+    SimpleResourceGraph graph;
+    graph << res;
+
+    m_dmModel->storeResources( graph, QLatin1String("testApp") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    QList<Statement> stList = m_model->listStatements( Node(), NIE::isPartOf(), Node() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl folderResUri = stList.first().object().uri();
+    QVERIFY( m_dmModel->containsAnyStatement( folderResUri, RDF::type(), NFO::FileDataObject() ) );
+    QVERIFY( m_dmModel->containsAnyStatement( folderResUri, RDF::type(), NFO::Folder() ) );
+    QVERIFY( m_dmModel->containsAnyStatement( folderResUri, NIE::url(), folderUrl ) );
+}
+
+
+void DataManagementModelTest::testStoreResources_fileExists()
+{
+    SimpleResource res(QUrl("file:///a/b/v/c/c"));
+    res.addType( NMM::MusicPiece() );
+    res.addProperty( NAO::numericRating(), 10 );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("app") );
+
+    // Should give an error - The file does not exist ( probably )
+    QVERIFY( m_dmModel->lastError() );
+}
+
+
+void DataManagementModelTest::testStoreResources_sameNieUrl()
+{
+    QTemporaryFile fileA;
+    fileA.open();
+    const QUrl fileUrl = QUrl::fromLocalFile(fileA.fileName());
+
+    SimpleResource res;
+    res.setUri( fileUrl );
+    res.addProperty( RDF::type(), NFO::FileDataObject() );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("app1") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    // Make sure the fileUrl got added
+    QList< Statement > stList = m_model->listStatements( Node(), NIE::url(), fileUrl ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    QUrl fileResUri = stList.first().subject().uri();
+
+    // Make sure there is only one rdf:type nfo:FileDataObject
+    stList = m_model->listStatements( Node(), RDF::type(), NFO::FileDataObject() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+    QCOMPARE( stList.first().subject().uri(), fileResUri );
+
+    SimpleResource res2;
+    res2.addProperty( NIE::url(), fileUrl );
+    res2.addProperty( NAO::numericRating(), QVariant(10) );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res2, QLatin1String("app1") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    // Make sure it got mapped
+    stList = m_model->listStatements( Node(), NAO::numericRating(), LiteralValue(10) ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+    QCOMPARE( stList.first().subject().uri(), fileResUri );
+}
+
+// metadata should be ignored when merging one resource into another
+void DataManagementModelTest::testStoreResources_metadata()
+{
+    // create our app
+    const QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create a resource
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    const QDateTime now = QDateTime::currentDateTime();
+    QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, RDF::type(), NAO::Tag(), g1);
+    m_model->addStatement(resA, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("Foobar")), g1);
+    m_model->addStatement(resA, QUrl("prop:/string2"), LiteralValue(QLatin1String("Foobar2")), g1);
+    m_model->addStatement(resA, NAO::created(), LiteralValue(now), g1);
+    m_model->addStatement(resA, NAO::lastModified(), LiteralValue(now), g1);
+
+
+    // now we merge the same resource (with differing metadata)
+    SimpleResource a;
+    a.addProperty(RDF::type(), NAO::Tag());
+    a.addProperty(QUrl("prop:/int"), QVariant(42));
+    a.addProperty(QUrl("prop:/string"), QVariant(QLatin1String("Foobar")));
+    QDateTime creationDateTime(QDate(2010, 12, 24), QTime::currentTime());
+    a.addProperty(NAO::created(), QVariant(creationDateTime));
+
+    // merge the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << a, QLatin1String("B"));
+    QVERIFY(!m_dmModel->lastError());
+
+    // make sure no new resource has been created
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NAO::Tag()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/int"), Node()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/string"), Node()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(resA, NAO::created(), Node()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(resA, NAO::lastModified(), Node()).allStatements().count(), 1);
+
+    // make sure the new app has been created
+    QueryResultIterator it = m_model->executeQuery(QString::fromLatin1("select ?a where { ?a a %1 . ?a %2 %3 . }")
+                                                   .arg(Node::resourceToN3(NAO::Agent()),
+                                                        Node::resourceToN3(NAO::identifier()),
+                                                        Node::literalToN3(QLatin1String("B"))),
+                                                   Soprano::Query::QueryLanguageSparql);
+    QVERIFY(it.next());
+    const QUrl appBRes = it[0].uri();
+
+    // make sure the data is now maintained by both apps
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { graph ?g { %1 <prop:/int> %2 . } . ?g %3 %4 . ?g %3 <app:/A> . }")
+                                  .arg(Node::resourceToN3(resA),
+                                       Node::literalToN3(42),
+                                       Node::resourceToN3(NAO::maintainedBy()),
+                                       Node::resourceToN3(appBRes)),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { graph ?g { %1 <prop:/string> %2 . } . ?g %3 %4 . ?g %3 <app:/A> . }")
+                                  .arg(Node::resourceToN3(resA),
+                                       Node::literalToN3(QLatin1String("Foobar")),
+                                       Node::resourceToN3(NAO::maintainedBy()),
+                                       Node::resourceToN3(appBRes)),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+    QVERIFY(m_model->executeQuery(QString::fromLatin1("ask where { graph ?g { %1 a %2 . } . ?g %3 %4 . ?g %3 <app:/A> . }")
+                                  .arg(Node::resourceToN3(resA),
+                                       Node::resourceToN3(NAO::Tag()),
+                                       Node::resourceToN3(NAO::maintainedBy()),
+                                       Node::resourceToN3(appBRes)),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+    QVERIFY(!m_model->executeQuery(QString::fromLatin1("ask where { graph ?g { %1 <prop:/string2> %2 . } . ?g %3 %4 . ?g %3 <app:/A> . }")
+                                  .arg(Node::resourceToN3(resA),
+                                       Node::literalToN3(QLatin1String("Foobar2")),
+                                       Node::resourceToN3(NAO::maintainedBy()),
+                                       Node::resourceToN3(appBRes)),
+                                  Soprano::Query::QueryLanguageSparql).boolValue());
+
+    // Make sure that the nao:lastModified and nao:created have not changed
+    QDateTime mod = m_model->listStatements( resA, NAO::lastModified(), Soprano::Node() ).iterateObjects().allNodes().first().literal().toDateTime();
+    QDateTime creation = m_model->listStatements( resA, NAO::created(), Soprano::Node() ).iterateObjects().allNodes().first().literal().toDateTime();
+
+    QCOMPARE( mod, now );
+    // The creation date for now will always stay the same.
+    // FIXME: Should we allow changes?
+    QVERIFY( creation != creationDateTime );
+
+    QVERIFY(!haveTrailingGraphs());
+
+
+    // now merge the same resource with some new data - just to make sure the metadata is updated properly
+    SimpleResource sResA(resA);
+    sResA.addProperty(RDF::type(), NAO::Tag());
+    sResA.addProperty(QUrl("prop:/int2"), 42);
+
+    // merge the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << sResA, QLatin1String("B"));
+    QVERIFY(!m_dmModel->lastError());
+
+    // make sure the new data is there
+    QVERIFY(m_model->containsAnyStatement(resA, QUrl("prop:/int2"), LiteralValue(42)));
+
+    // make sure creation date did not change
+    QCOMPARE(m_model->listStatements(resA, NAO::created(), Node()).allStatements().count(), 1);
+    QVERIFY(m_model->containsAnyStatement(resA, NAO::created(), LiteralValue(now)));
+
+    // make sure mtime has changed - the resource has changed
+    QCOMPARE(m_model->listStatements(resA, NAO::lastModified(), Node()).allStatements().count(), 1);
+    QVERIFY(m_model->listStatements(resA, NAO::lastModified(), Node()).iterateObjects().allNodes().first().literal().toDateTime() != now);
+}
+
+void DataManagementModelTest::testStoreResources_protectedTypes()
+{
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // property
+    SimpleResource propertyRes(QUrl("prop:/res"));
+    propertyRes.addProperty(QUrl("prop:/int"), 42);
+
+    // store the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << propertyRes, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // class
+    SimpleResource classRes(NRL::Graph());
+    classRes.addProperty(QUrl("prop:/int"), 42);
+
+    // store the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << classRes, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // graph
+    SimpleResource graphRes(QUrl("graph:/onto"));
+    propertyRes.addProperty(QUrl("prop:/int"), 42);
+
+    // store the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << graphRes, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+// make sure storeResources ignores supertypes
+void DataManagementModelTest::testStoreResources_superTypes()
+{
+    // 1. create a resource to merge
+    QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("hello world")), g1);
+    m_model->addStatement(resA, RDF::type(), QUrl("class:/typeA"), g1);
+    m_model->addStatement(resA, RDF::type(), QUrl("class:/typeB"), g1);
+    const QDateTime now = QDateTime::currentDateTime();
+    m_model->addStatement(resA, NAO::created(), LiteralValue(now), g1);
+    m_model->addStatement(resA, NAO::lastModified(), LiteralValue(now), g1);
+
+
+    // now merge the same resource (excluding the super-type A)
+    SimpleResource a;
+    a.addProperty(RDF::type(), QUrl("class:/typeB"));
+    a.addProperty(QUrl("prop:/string"), QLatin1String("hello world"));
+
+    m_dmModel->storeResources(SimpleResourceGraph() << a, QLatin1String("A"));
+    QVERIFY( !m_dmModel->lastError() );
+
+
+    // make sure the existing resource was reused
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/string"), LiteralValue(QLatin1String("hello world"))).allElements().count(), 1);
+}
+
+// make sure merging even works with missing metadata in store
+void DataManagementModelTest::testStoreResources_missingMetadata()
+{
+    // create our app
+    const QUrl appG = m_nrlModel->createGraph(NRL::InstanceBase());
+    m_model->addStatement(QUrl("app:/A"), RDF::type(), NAO::Agent(), appG);
+    m_model->addStatement(QUrl("app:/A"), NAO::identifier(), LiteralValue(QLatin1String("A")), appG);
+
+    // create a resource (without creation date)
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+    m_model->addStatement(g1, NAO::maintainedBy(), QUrl("app:/A"), mg1);
+
+    const QDateTime now = QDateTime::currentDateTime();
+    QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, RDF::type(), NAO::Tag(), g1);
+    m_model->addStatement(resA, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("Foobar")), g1);
+    m_model->addStatement(resA, NAO::lastModified(), LiteralValue(now), g1);
+
+
+    // now we merge the same resource
+    SimpleResource a;
+    a.addProperty(RDF::type(), NAO::Tag());
+    a.addProperty(QUrl("prop:/int"), QVariant(42));
+    a.addProperty(QUrl("prop:/string"), QVariant(QLatin1String("Foobar")));
+
+    // merge the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << a, QLatin1String("B"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // make sure no new resource has been created
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), NAO::Tag()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/int"), Node()).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/string"), Node()).allStatements().count(), 1);
+
+    QVERIFY(!haveTrailingGraphs());
+
+
+    // now merge the same resource with some new data - just to make sure the metadata is updated properly
+    SimpleResource simpleResA(resA);
+    simpleResA.addProperty(RDF::type(), NAO::Tag());
+    simpleResA.addProperty(QUrl("prop:/int2"), 42);
+
+    // merge the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << simpleResA, QLatin1String("B"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // make sure the new data is there
+    QVERIFY(m_model->containsAnyStatement(resA, QUrl("prop:/int2"), LiteralValue(42)));
+
+    // make sure creation date did not change, ie. it was not created as that would be wrong
+    QVERIFY(!m_model->containsAnyStatement(resA, NAO::created(), Node()));
+
+    // make sure the last mtime has been updated
+    QCOMPARE(m_model->listStatements(resA, NAO::lastModified(), Node()).allStatements().count(), 1);
+    QDateTime newDt = m_model->listStatements(resA, NAO::lastModified(), Node()).iterateObjects().allNodes().first().literal().toDateTime();
+    QVERIFY( newDt > now);
+
+    //
+    // Merge the resource again, but this time make sure it is identified as well
+    //
+    SimpleResource resB;
+    resB.addProperty(RDF::type(), NAO::Tag());
+    resB.addProperty(QUrl("prop:/int"), QVariant(42));
+    resB.addProperty(QUrl("prop:/string"), QVariant(QLatin1String("Foobar")));
+    resB.addProperty(QUrl("prop:/int2"), 42);
+    resB.addProperty(QUrl("prop:/int3"), 50);
+
+    // merge the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << resB, QLatin1String("B"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // make sure the new data is there
+    QVERIFY(m_model->containsAnyStatement(resA, QUrl("prop:/int3"), LiteralValue(50)));
+
+    // make sure creation date did not change, ie. it was not created as that would be wrong
+    QVERIFY(!m_model->containsAnyStatement(resA, NAO::created(), Node()));
+
+    // make sure the last mtime has been updated
+    QCOMPARE(m_model->listStatements(resA, NAO::lastModified(), Node()).allStatements().count(), 1);
+    QVERIFY(m_model->listStatements(resA, NAO::lastModified(), Node()).iterateObjects().allNodes().first().literal().toDateTime() > newDt);
+}
+
+// test merging when there is more than one candidate resource to merge with
+void DataManagementModelTest::testStoreResources_multiMerge()
+{
+    // create two resource which could be matches for the one we will store
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    // the resource in which we want to merge
+    QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resA, RDF::type(), NAO::Tag(), g1);
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(resA, NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(resA, NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    QUrl resB("nepomuk:/res/B");
+    m_model->addStatement(resB, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resB, RDF::type(), NAO::Tag(), g1);
+    m_model->addStatement(resB, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(resB, NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(resB, NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+
+    // now store the exact same resource
+    SimpleResource res;
+    res.addProperty(RDF::type(), NAO::Tag());
+    res.addProperty(QUrl("prop:/int"), 42);
+    res.addProperty(QUrl("prop:/string"), QLatin1String("foobar"));
+
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("A"));
+    QVERIFY(!m_dmModel->lastError());
+
+    // make sure no new resource was created
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/int"), LiteralValue(42)).allElements().count(), 2);
+    QCOMPARE(m_model->listStatements(Node(), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))).allElements().count(), 2);
+
+    // make sure both resources still exist
+    QVERIFY(m_model->containsAnyStatement(resA, Node(), Node()));
+    QVERIFY(m_model->containsAnyStatement(resB, Node(), Node()));
+}
+
+// an example from real-life which made an early version of DMS fail
+void DataManagementModelTest::testStoreResources_realLife()
+{
+    // we deal with one file
+    QTemporaryFile theFile;
+    theFile.open();
+
+    // the full data - slightly cleanup up (excluding additional video track resources)
+    // this data is a combination from the file indexing service and DMS storeResources calls
+
+    // the resource URIs used
+    const QUrl fileResUri("nepomuk:/res/3ff603a5-4023-4c2f-bd89-372002a0ffd2");
+    const QUrl tvSeriesUri("nepomuk:/res/e6fbe22d-bb5c-416c-a935-407a34b58c76");
+    const QUrl appRes("nepomuk:/res/275907b0-c120-4581-83d5-ea9ec034dbcd");
+
+    // the graph URIs
+    // two strigi graphs due to the nie:url preservation
+    const QUrl strigiG1("nepomuk:/ctx/5ca62cd0-ccff-4484-99e3-1fd9f782a3a4");
+    const QUrl strigiG2("nepomuk:/ctx/9f0bac21-f8e4-4e82-b51d-e0a7585f1c8d");
+    const QUrl strigiMG1("nepomuk:/ctx/0117104c-d501-48ce-badd-6f363bfde3e2");
+    const QUrl strigiMG2("nepomuk:/ctx/e52c8b8a-2e32-4a27-8633-03f27fec441b");
+
+    const QUrl dmsG1("nepomuk:/ctx/8bea556f-cacf-4f31-be73-7f7c0f14024b");
+    const QUrl dmsG2("nepomuk:/ctx/374d3968-0d20-4807-8a87-d2d6b87e7de3");
+    const QUrl dmsMG1("nepomuk:/ctx/9902847f-dfe8-489a-881b-4abf1707fee7");
+    const QUrl dmsMG2("nepomuk:/ctx/72ef2cdf-26e7-42b6-9093-0b7b0a7c25fc");
+
+    const QUrl appG1("nepomuk:/ctx/7dc9f013-4e45-42bf-8595-a12e78adde81");
+    const QUrl appMG1("nepomuk:/ctx/1ffcb2bb-525d-4173-b211-ebdf28c0897b");
+
+    // strings we reuse
+    const QString seriesTitle("Nepomuk The Series");
+    const QString episodeTitle("Who are you?");
+    const QString seriesOverview("Nepomuk is a series about information and the people needing this information for informational purposes.");
+    const QString episodeOverview("The series pilot focusses on this and that, nothing in particular and it not really that interesting at all.");
+
+    // the file resource
+    m_model->addStatement(fileResUri, NIE::isPartOf(), QUrl("nepomuk:/res/e9f85f29-150d-49b6-9ffb-264ae7ec3864"), strigiG1);
+    m_model->addStatement(fileResUri, NIE::contentSize(), Soprano::LiteralValue::fromString("369532928", XMLSchema::xsdInt()), strigiG1);
+    m_model->addStatement(fileResUri, NIE::mimeType(), Soprano::LiteralValue::fromString("audio/x-riff", XMLSchema::string()), strigiG1);
+    m_model->addStatement(fileResUri, NIE::mimeType(), Soprano::LiteralValue::fromString("video/x-msvideo", XMLSchema::string()), strigiG1);
+    m_model->addStatement(fileResUri, NFO::fileName(), Soprano::LiteralValue(KUrl(theFile.fileName()).fileName()), strigiG1);
+    m_model->addStatement(fileResUri, NIE::lastModified(), Soprano::LiteralValue::fromString("2010-06-29T15:44:44Z", XMLSchema::dateTime()), strigiG1);
+    m_model->addStatement(fileResUri, NFO::codec(), Soprano::LiteralValue::fromString("MP3", XMLSchema::string()), strigiG1);
+    m_model->addStatement(fileResUri, NFO::codec(), Soprano::LiteralValue::fromString("xvid", XMLSchema::string()), strigiG1);
+    m_model->addStatement(fileResUri, NFO::averageBitrate(), Soprano::LiteralValue::fromString("1132074", XMLSchema::xsdInt()), strigiG1);
+    m_model->addStatement(fileResUri, NFO::duration(), Soprano::LiteralValue::fromString("2567", XMLSchema::xsdInt()), strigiG1);
+    m_model->addStatement(fileResUri, NFO::duration(), Soprano::LiteralValue::fromString("2611", XMLSchema::xsdInt()), strigiG1);
+    m_model->addStatement(fileResUri, NFO::frameRate(), Soprano::LiteralValue::fromString("23", XMLSchema::xsdInt()), strigiG1);
+    m_model->addStatement(fileResUri, NIE::hasPart(), QUrl("nepomuk:/res/b805e3bb-db13-4561-b457-8da8d13ce34d"), strigiG1);
+    m_model->addStatement(fileResUri, NIE::hasPart(), QUrl("nepomuk:/res/c438df3c-1446-4931-9d9e-3665567025b9"), strigiG1);
+    m_model->addStatement(fileResUri, NFO::horizontalResolution(), Soprano::LiteralValue::fromString("624", XMLSchema::xsdInt()), strigiG1);
+    m_model->addStatement(fileResUri, NFO::verticalResolution(), Soprano::LiteralValue::fromString("352", XMLSchema::xsdInt()), strigiG1);
+
+    m_model->addStatement(fileResUri, RDF::type(), NFO::FileDataObject(), strigiG2);
+    m_model->addStatement(fileResUri, NIE::url(), QUrl::fromLocalFile(theFile.fileName()), strigiG2);
+
+    m_model->addStatement(fileResUri, RDF::type(), NMM::TVShow(), dmsG1);
+    m_model->addStatement(fileResUri, NAO::created(), Soprano::LiteralValue::fromString("2011-03-14T10:06:38.317Z", XMLSchema::dateTime()), dmsG1);
+    m_model->addStatement(fileResUri, NIE::title(), Soprano::LiteralValue(episodeTitle), dmsG1);
+    m_model->addStatement(fileResUri, NAO::lastModified(), Soprano::LiteralValue::fromString("2011-03-14T10:06:38.317Z", XMLSchema::dateTime()), dmsG1);
+    m_model->addStatement(fileResUri, NMM::synopsis(), Soprano::LiteralValue(episodeOverview), dmsG1);
+    m_model->addStatement(fileResUri, NMM::series(), tvSeriesUri, dmsG1);
+    m_model->addStatement(fileResUri, NMM::season(), Soprano::LiteralValue::fromString("1", XMLSchema::xsdInt()), dmsG1);
+    m_model->addStatement(fileResUri, NMM::episodeNumber(), Soprano::LiteralValue::fromString("1", XMLSchema::xsdInt()), dmsG1);
+    m_model->addStatement(fileResUri, RDF::type(), NIE::InformationElement(), dmsG2);
+    m_model->addStatement(fileResUri, RDF::type(), NFO::Video(), dmsG2);
+
+    // the TV Series resource
+    m_model->addStatement(tvSeriesUri, RDF::type(), NMM::TVSeries(), dmsG1);
+    m_model->addStatement(tvSeriesUri, NAO::created(), Soprano::LiteralValue::fromString("2011-03-14T10:06:38.317Z", XMLSchema::dateTime()), dmsG1);
+    m_model->addStatement(tvSeriesUri, NIE::title(), Soprano::LiteralValue(seriesTitle), dmsG1);
+    m_model->addStatement(tvSeriesUri, NAO::lastModified(), Soprano::LiteralValue::fromString("2011-03-14T10:06:38.317Z", XMLSchema::dateTime()), dmsG1);
+    m_model->addStatement(tvSeriesUri, NIE::description(), Soprano::LiteralValue(seriesOverview), dmsG1);
+    m_model->addStatement(tvSeriesUri, NMM::hasEpisode(), fileResUri, dmsG1);
+    m_model->addStatement(tvSeriesUri, RDF::type(), NIE::InformationElement(), dmsG2);
+
+    // the app that called storeResources
+    m_model->addStatement(appRes, RDF::type(), NAO::Agent(), appG1);
+    m_model->addStatement(appRes, NAO::prefLabel(), Soprano::LiteralValue::fromString("Nepomuk TVNamer", XMLSchema::string()), appG1);
+    m_model->addStatement(appRes, NAO::identifier(), Soprano::LiteralValue::fromString("nepomuktvnamer", XMLSchema::string()), appG1);
+
+    // all the graph metadata
+    m_model->addStatement(strigiG1, RDF::type(), NRL::DiscardableInstanceBase(), strigiMG1);
+    m_model->addStatement(strigiG1, NAO::created(), Soprano::LiteralValue::fromString("2010-10-22T14:13:42.204Z", XMLSchema::dateTime()), strigiMG1);
+    m_model->addStatement(strigiG1, QUrl("http://www.strigi.org/fields#indexGraphFor"), fileResUri, strigiMG1);
+    m_model->addStatement(strigiMG1, RDF::type(), NRL::GraphMetadata(), strigiMG1);
+    m_model->addStatement(strigiMG1, NRL::coreGraphMetadataFor(), strigiG1, strigiMG1);
+
+    m_model->addStatement(strigiG2, RDF::type(), NRL::InstanceBase(), strigiMG2);
+    m_model->addStatement(strigiG2, NAO::created(), Soprano::LiteralValue::fromString("2010-10-22T14:13:42.204Z", XMLSchema::dateTime()), strigiMG2);
+    m_model->addStatement(strigiG2, QUrl("http://www.strigi.org/fields#indexGraphFor"), fileResUri, strigiMG2);
+    m_model->addStatement(strigiG2, NAO::maintainedBy(), appRes, strigiMG2);
+    m_model->addStatement(strigiMG2, RDF::type(), NRL::GraphMetadata(), strigiMG2);
+    m_model->addStatement(strigiMG2, NRL::coreGraphMetadataFor(), strigiG2, strigiMG2);
+
+    m_model->addStatement(dmsG1, RDF::type(), NRL::InstanceBase(), dmsMG1);
+    m_model->addStatement(dmsG1, NAO::created(), Soprano::LiteralValue::fromString("2011-03-14T10:06:38.343Z", XMLSchema::dateTime()), dmsMG1);
+    m_model->addStatement(dmsG1, NAO::maintainedBy(), appRes, dmsMG1);
+    m_model->addStatement(dmsMG1, RDF::type(), NRL::GraphMetadata(), dmsMG1);
+    m_model->addStatement(dmsMG1, NRL::coreGraphMetadataFor(), dmsG1, dmsMG1);
+
+    m_model->addStatement(dmsG2, RDF::type(), NRL::InstanceBase(), dmsMG2);
+    m_model->addStatement(dmsG2, NAO::created(), Soprano::LiteralValue::fromString("2011-03-14T10:06:38.621Z", XMLSchema::dateTime()), dmsMG2);
+    m_model->addStatement(dmsG2, NAO::maintainedBy(), appRes, dmsMG2);
+    m_model->addStatement(dmsMG2, RDF::type(), NRL::GraphMetadata(), dmsMG2);
+    m_model->addStatement(dmsMG2, NRL::coreGraphMetadataFor(), dmsG2, dmsMG2);
+
+    m_model->addStatement(appG1, RDF::type(), NRL::InstanceBase(), appMG1);
+    m_model->addStatement(appG1, NAO::created(), Soprano::LiteralValue::fromString("2011-03-12T17:48:44.307Z", XMLSchema::dateTime()), appMG1);
+    m_model->addStatement(appMG1, RDF::type(), NRL::GraphMetadata(), appMG1);
+    m_model->addStatement(appMG1, NRL::coreGraphMetadataFor(), appG1, appMG1);
+
+
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // now the TV show information is stored again
+    SimpleResourceGraph graph;
+    SimpleResource tvShowRes(QUrl::fromLocalFile(theFile.fileName()));
+    tvShowRes.addProperty(RDF::type(), NMM::TVShow());
+    tvShowRes.addProperty(NMM::episodeNumber(), 1);
+    tvShowRes.addProperty(NMM::season(), 1);
+    tvShowRes.addProperty(NIE::title(), episodeTitle);
+    tvShowRes.addProperty(NMM::synopsis(), episodeOverview);
+
+    SimpleResource tvSeriesRes;
+    tvSeriesRes.addProperty(RDF::type(), NMM::TVSeries());
+    tvSeriesRes.addProperty(NIE::title(), seriesTitle);
+    tvSeriesRes.addProperty(NIE::description(), seriesOverview);
+    tvSeriesRes.addProperty(NMM::hasEpisode(), tvShowRes.uri());
+
+    tvShowRes.addProperty(NMM::series(), tvSeriesRes.uri());
+
+    graph << tvShowRes << tvSeriesRes;
+
+    m_dmModel->storeResources(graph, QLatin1String("nepomuktvnamer"));
+    QVERIFY(!m_dmModel->lastError());
+
+    // now test the data - nothing should have changed at all
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+void DataManagementModelTest::testStoreResources_trivialMerge()
+{
+    // we create a resource with some properties
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, RDF::type(), QUrl("class:/typeA"), g1);
+    m_model->addStatement(resA, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(resA, NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(resA, NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+
+    // now we store a trivial resource
+    SimpleResource res;
+    res.addProperty(RDF::type(), QUrl("class:/typeA"));
+
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("A"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // the two resources should NOT have been merged
+    QCOMPARE(m_model->listStatements(Node(), RDF::type(), QUrl("class:/typeA")).allElements().count(), 2);
+}
+
+// make sure that two resources are not merged if they have no matching type even if the rest of the idenfifying props match.
+// the merged resource does not have any type
+void DataManagementModelTest::testStoreResources_noTypeMatch1()
+{
+    // we create a resource with some properties
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, RDF::type(), QUrl("class:/typeA"), g1);
+    m_model->addStatement(resA, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(resA, NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(resA, NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    // now we store the resource without a type
+    SimpleResource res;
+    res.addProperty(QUrl("prop:/int"), 42);
+    res.addProperty(QUrl("prop:/string"), QLatin1String("foobar"));
+
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("A"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // the two resources should NOT have been merged - we should have a new resource
+    QCOMPARE(m_model->listStatements(Soprano::Node(), QUrl("prop:/int"), Soprano::LiteralValue(42)).allStatements().count(), 2);
+    QCOMPARE(m_model->listStatements(Soprano::Node(), QUrl("prop:/string"), Soprano::LiteralValue(QLatin1String("foobar"))).allStatements().count(), 2);
+
+    // two different subjects
+    QCOMPARE(m_model->listStatements(Soprano::Node(), QUrl("prop:/int"), Soprano::LiteralValue(42)).iterateSubjects().allNodes().toSet().count(), 2);
+}
+
+// make sure that two resources are not merged if they have no matching type even if the rest of the idenfifying props match.
+// the merged resource has a different type than the one in store
+void DataManagementModelTest::testStoreResources_noTypeMatch2()
+{
+    // we create a resource with some properties
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, RDF::type(), QUrl("class:/typeA"), g1);
+    m_model->addStatement(resA, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(resA, NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+    m_model->addStatement(resA, NAO::lastModified(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+    // now we store the resource with a different type
+    SimpleResource res;
+    res.addType(QUrl("class:/typeB"));
+    res.addProperty(QUrl("prop:/int"), 42);
+    res.addProperty(QUrl("prop:/string"), QLatin1String("foobar"));
+
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("A"));
+    QVERIFY( !m_dmModel->lastError() );
+
+    // the two resources should NOT have been merged - we should have a new resource
+    QCOMPARE(m_model->listStatements(Soprano::Node(), QUrl("prop:/int"), Soprano::LiteralValue(42)).allStatements().count(), 2);
+    QCOMPARE(m_model->listStatements(Soprano::Node(), QUrl("prop:/string"), Soprano::LiteralValue(QLatin1String("foobar"))).allStatements().count(), 2);
+
+    // two different subjects
+    QCOMPARE(m_model->listStatements(Soprano::Node(), QUrl("prop:/int"), Soprano::LiteralValue(42)).iterateSubjects().allNodes().toSet().count(), 2);
+}
+
+void DataManagementModelTest::testStoreResources_faultyMetadata()
+{
+    KTemporaryFile file;
+    file.open();
+    const QUrl fileUrl( file.fileName() );
+
+    SimpleResource res;
+    res.addProperty( RDF::type(), NFO::FileDataObject() );
+    res.addProperty( NIE::url(), fileUrl );
+    res.addProperty( NAO::lastModified(), QVariant( 5 ) );
+    res.addProperty( NAO::created(), QVariant(QLatin1String("oh no") ) );
+
+    QList<Soprano::Statement> list = m_model->listStatements().allStatements();
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("testApp") );
+
+    // The should be an error
+    QVERIFY(m_dmModel->lastError());
+
+    // And the statements should not exist
+    QVERIFY(!m_model->containsAnyStatement( Node(), RDF::type(), NFO::FileDataObject() ));
+    QVERIFY(!m_model->containsAnyStatement( Node(), NIE::url(), fileUrl ));
+    QVERIFY(!m_model->containsAnyStatement( Node(), NAO::lastModified(), Node() ));
+    QVERIFY(!m_model->containsAnyStatement( Node(), NAO::created(), Node() ));
+
+    QList<Soprano::Statement> list2 = m_model->listStatements().allStatements();
+    QCOMPARE( list, list2 );
+}
+
+void DataManagementModelTest::testStoreResources_additionalMetadataApp()
+{
+    KTemporaryFile file;
+    file.open();
+    const QUrl fileUrl( file.fileName() );
+
+    SimpleResource res;
+    res.addProperty( RDF::type(), NFO::FileDataObject() );
+    res.addProperty( NIE::url(), fileUrl );
+
+    SimpleResource app;
+    app.addProperty( RDF::type(), NAO::Agent() );
+    app.addProperty( NAO::identifier(), "appB" );
+
+    SimpleResourceGraph g;
+    g << res << app;
+
+    QHash<QUrl, QVariant> additionalMetadata;
+    additionalMetadata.insert( NAO::maintainedBy(), app.uri() );
+
+    m_dmModel->storeResources( g, QLatin1String("appA"), Nepomuk::IdentifyNew, Nepomuk::NoStoreResourcesFlags, additionalMetadata );
+
+    //FIXME: for now this should fail as nao:maintainedBy is protected,
+    //       but what if we want to add some additionalMetadata which references
+    //       a node in the SimpleResourceGraph. There needs to be a test for that.
+    QVERIFY(m_dmModel->lastError());
+}
+
+void DataManagementModelTest::testStoreResources_itemUris()
+{
+    SimpleResourceGraph g;
+
+    for (int i = 0; i < 10; i++) {
+        QUrl uri( "testuri:?item="+QString::number(i) );
+        SimpleResource r(uri);
+        r.addType( NIE::DataObject() );
+        r.addType( NIE::InformationElement() );
+
+        QString label = QLatin1String("label") + QString::number(i);
+        r.setProperty( NAO::prefLabel(), label );
+        g.insert(r);
+    }
+
+    m_dmModel->storeResources( g, "app" );
+
+    // Should give an error 'testuri' is an unknown protocol
+    QVERIFY(m_dmModel->lastError());
+}
+
+void DataManagementModelTest::testStoreResources_kioProtocols()
+{
+    QStringList protocolList = KProtocolInfo::protocols();
+    protocolList.removeAll( QLatin1String("nepomuk") );
+    protocolList.removeAll( QLatin1String("file") );
+
+    kDebug() << "List: " << protocolList;
+    foreach( const QString& protocol, protocolList ) {
+        SimpleResource res( QUrl(protocol + ":/item") );
+        res.addType( NFO::FileDataObject() );
+        res.addType( NMM::MusicPiece() );
+
+        m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("app") );
+        QVERIFY(!m_dmModel->lastError());
+
+        QVERIFY( m_model->containsAnyStatement( Node(), NIE::url(), res.uri() ) );
+
+        const QUrl resUri = m_model->listStatements( Node(), NIE::url(), res.uri() ).allStatements().first().subject().uri();
+
+        QVERIFY( m_model->containsAnyStatement( resUri, RDF::type(), NFO::FileDataObject() ) );
+        QVERIFY( m_model->containsAnyStatement( resUri, RDF::type(), NMM::MusicPiece() ) );
+    }
+}
+
+
+void DataManagementModelTest::testStoreResources_duplicates()
+{
+    KTemporaryFile file;
+    file.open();
+    const QUrl fileUrl( file.fileName() );
+
+    SimpleResource res;
+    res.addType( NFO::FileDataObject() );
+    res.addProperty( NIE::url(), fileUrl );
+
+    SimpleResource hash1;
+    hash1.addType( NFO::FileHash() );
+    hash1.addProperty( NFO::hashAlgorithm(), QLatin1String("SHA1") );
+    hash1.addProperty( NFO::hashValue(), QLatin1String("ddaa6b339428b75ee1545f80f1f35fb89c166bf9") );
+
+    SimpleResource hash2;
+    hash2.addType( NFO::FileHash() );
+    hash2.addProperty( NFO::hashAlgorithm(), QLatin1String("SHA1") );
+    hash2.addProperty( NFO::hashValue(), QLatin1String("ddaa6b339428b75ee1545f80f1f35fb89c166bf9") );
+
+    res.addProperty( NFO::hasHash(), hash1.uri() );
+    res.addProperty( NFO::hasHash(), hash2.uri() );
+
+    SimpleResourceGraph graph;
+    graph << res << hash1 << hash2;
+
+    m_dmModel->storeResources( graph, "appA" );
+    QVERIFY(!m_dmModel->lastError());
+
+    // hash1 and hash2 are the same, they should have been merged together
+    int hashCount = m_model->listStatements( Node(), RDF::type(), NFO::FileHash() ).allStatements().size();
+    QCOMPARE( hashCount, 1 );
+
+    // res should have only have one hash1
+    QCOMPARE( m_model->listStatements( Node(), NFO::hasHash(), Node() ).allStatements().size(), 1 );
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testStoreResources_overwriteProperties()
+{
+    SimpleResource contact;
+    contact.addType( NCO::Contact() );
+    contact.addProperty( NCO::fullname(), QLatin1String("Spiderman") );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << contact, QLatin1String("app") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    QList< Statement > stList = m_model->listStatements( Node(), RDF::type(), NCO::Contact() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl resUri = stList.first().subject().uri();
+
+    SimpleResource contact2( resUri );
+    contact2.addType( NCO::Contact() );
+    contact2.addProperty( NCO::fullname(), QLatin1String("Peter Parker") );
+
+    //m_dmModel->storeResources( SimpleResourceGraph() << contact2, QLatin1String("app") );
+    //QVERIFY( m_dmModel->lastError() ); // should fail without the merge flags
+
+    // Now everyone will know who Spiderman really is
+    m_dmModel->storeResources( SimpleResourceGraph() << contact2, QLatin1String("app"), IdentifyNew, OverwriteProperties );
+    QVERIFY( !m_dmModel->lastError() );
+
+    stList = m_model->listStatements( resUri, NCO::fullname(), Node() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    QString newName = stList.first().object().literal().toString();
+    QCOMPARE( newName, QLatin1String("Peter Parker") );
+}
+
+// make sure that already existing resource types are taken into account for domain checks
+void DataManagementModelTest::testStoreResources_correctDomainInStore()
+{
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    // create the resource
+    const QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, RDF::type(), NMM::MusicPiece(), g1);
+    m_model->addStatement(resA, NAO::lastModified(), Soprano::LiteralValue(QDateTime::currentDateTime()), g1);
+
+    // now store a music piece with a performer.
+    // the performer does not have a type in the simple res but only in store
+    SimpleResource piece(resA);
+    piece.addProperty(NIE::title(), QLatin1String("Hello World"));
+    SimpleResource artist;
+    artist.addType(NCO::Contact());
+    artist.addProperty(NCO::fullname(), QLatin1String("foobar"));
+    piece.addProperty(NMM::performer(), artist);
+
+    m_dmModel->storeResources(SimpleResourceGraph() << piece << artist, QLatin1String("testapp"));
+
+    QVERIFY(!m_dmModel->lastError());
+}
+
+void DataManagementModelTest::testStoreResources_correctDomainInStore2()
+{
+    SimpleResource res;
+    res.addType( NMM::MusicPiece() );
+    res.addType( NFO::FileDataObject() );
+    res.addProperty( NIE::title(), QLatin1String("Music") );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("testApp") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    QList<Soprano::Statement> stList = m_model->listStatements( Node(), RDF::type(), NFO::FileDataObject() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl resUri = stList.first().subject().uri();
+
+    SimpleResource musicPiece;
+    musicPiece.addType( NFO::FileDataObject() );
+    // We're not giving it a nmm:MusicPiece type
+    musicPiece.addProperty( NIE::title(), QLatin1String("Music") );
+
+    SimpleResource artist;
+    artist.addType( NCO::Contact() );
+    artist.addProperty( NCO::fullname(), QLatin1String("Snow Patrol") );
+
+    // nmm:performer has a domain of nmm:MusicPiece which is already present in the store
+    musicPiece.addProperty( NMM::performer(), artist );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << musicPiece << artist,
+                               QLatin1String("testApp") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    // musicPiece should have gotten identified as res
+    stList = m_model->listStatements( Node(), RDF::type(), NFO::FileDataObject() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl musicPieceUri = stList.first().subject().uri();
+    QCOMPARE( musicPieceUri, resUri );
+
+    // It should have the artist
+    QVERIFY( m_model->containsAnyStatement( musicPieceUri, NMM::performer(), Node() ) );
+}
+
+// make sure that already existing resource types are taken into account for range checks
+void DataManagementModelTest::testStoreResources_correctRangeInStore()
+{
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    // create the resource
+    const QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, RDF::type(), NCO::Contact(), g1);
+    m_model->addStatement(resA, NAO::lastModified(), Soprano::LiteralValue(QDateTime::currentDateTime()), g1);
+
+    // now store a music piece with a performer.
+    // the performer does not have a type in the simple res but only in store
+    SimpleResource piece;
+    piece.addType(NMM::MusicPiece());
+    piece.addProperty(NIE::title(), QLatin1String("Hello World"));
+    SimpleResource artist(resA);
+    artist.addProperty(NCO::fullname(), QLatin1String("foobar"));
+    piece.addProperty(NMM::performer(), artist);
+
+    m_dmModel->storeResources(SimpleResourceGraph() << piece << artist, QLatin1String("testapp"));
+
+    QVERIFY(!m_dmModel->lastError());
+}
+
+
+void DataManagementModelTest::testStoreResources_correctRangeInStore2()
+{
+    SimpleResource res;
+    res.addType( NCO::Contact() );
+    res.addType( NFO::FileDataObject() );
+    res.addProperty( NCO::fullname(), QLatin1String("Jack Black") );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("testApp") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    QList<Soprano::Statement> stList = m_model->listStatements( Node(), RDF::type(), NFO::FileDataObject() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl resUri = stList.first().subject().uri();
+
+    SimpleResource musicPiece;
+    musicPiece.addType( NFO::FileDataObject() );
+    musicPiece.addType( NMM::MusicPiece() );
+    musicPiece.addProperty( NIE::title(), QLatin1String("Music") );
+
+    SimpleResource artist;
+    artist.addType( NFO::FileDataObject() );
+    // We're not giving it the type NCO::Contact - should be inferred from the store
+    artist.addProperty( NCO::fullname(), QLatin1String("Jack Black") );
+
+    // nmm:performer has a range of nco:Contact which is already present in the store
+    musicPiece.addProperty( NMM::performer(), artist );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << musicPiece << artist,
+                               QLatin1String("testApp") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    // artist should have gotten identified as res
+    stList = m_model->listStatements( Node(), RDF::type(), NCO::Contact() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl artistUri = stList.first().subject().uri();
+    QCOMPARE( artistUri, resUri );
+
+    // It should have the artist
+    QVERIFY( m_model->containsAnyStatement( Node(), NMM::performer(), artistUri ) );
+}
+
+// make sure that the same values are simply merged even if encoded differently
+void DataManagementModelTest::testStoreResources_duplicateValuesAsString()
+{
+    SimpleResource res;
+
+    // add the same type twice
+    res.addType(QUrl("class:/typeA"));
+    res.addProperty(RDF::type(), QLatin1String("class:/typeA"));
+
+    // add the same value twice
+    res.addProperty(QUrl("prop:/int"), 42);
+    res.addProperty(QUrl("prop:/int"), QLatin1String("42"));
+
+    // now add the resource
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("testapp"));
+
+    // this should succeed
+    QVERIFY(!m_dmModel->lastError());
+
+    // make sure all is well
+    QCOMPARE(m_model->listStatements(Soprano::Node(), RDF::type(), QUrl("class:/typeA")).allStatements().count(), 1);
+    QCOMPARE(m_model->listStatements(Soprano::Node(), QUrl("prop:/int"), LiteralValue(42)).allStatements().count(), 1);
+}
+
+void DataManagementModelTest::testStoreResources_ontology()
+{
+    SimpleResource res( NFO::FileDataObject() );
+    res.addType( NCO::Contact() );
+
+    m_dmModel->storeResources(SimpleResourceGraph() << res, QLatin1String("testapp"));
+
+    // There should be some error, we're trying to set an ontology
+    QVERIFY( m_dmModel->lastError() );
+}
+
+void DataManagementModelTest::testStoreResources_legacyUris()
+{
+    const QUrl uri("res:/A");
+
+    const QUrl graphUri = m_nrlModel->createGraph( NRL::InstanceBase() );
+    m_model->addStatement( uri, RDF::type(), NFO::FileDataObject(), graphUri );
+    m_model->addStatement( uri, RDF::type(), NFO::Folder(), graphUri );
+    m_model->addStatement( uri, NAO::numericRating(), LiteralValue(5), graphUri );
+
+    SimpleResource res( uri );
+    res.addType( NFO::Folder() );
+    res.addType( NFO::FileDataObject() );
+    res.addProperty( NAO::numericRating(), QLatin1String("5") );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("app"), IdentifyNew, OverwriteProperties );
+    QVERIFY( !m_dmModel->lastError() );
+
+    QVERIFY( m_model->containsAnyStatement( uri, NAO::numericRating(), LiteralValue(5) ) );
+
+    SimpleResource res2;
+    res2.addType( NFO::FileDataObject() );
+    res2.addProperty( NIE::isPartOf(), uri );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res2, QLatin1String("app") );
+    QVERIFY( !m_dmModel->lastError() );
+
+    QVERIFY( m_model->containsAnyStatement( Node(), NIE::isPartOf(), uri ) );
+}
+
+void DataManagementModelTest::testStoreResources_lazyCardinalities()
+{
+    SimpleResource res;
+    res.addType( NCO::Contact() );
+    res.addProperty( NCO::fullname(), QLatin1String("Superman") );
+    res.addProperty( NCO::fullname(), QLatin1String("Clark Kent") ); // Don't tell Lex!
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("testApp"),
+                               Nepomuk::IdentifyNew, Nepomuk::LazyCardinalities );
+
+    // There shouldn't be any error, even though nco:fullname has maxCardinality = 1
+    QVERIFY( !m_dmModel->lastError() );
+
+    QList< Statement > stList = m_model->listStatements( Node(), NCO::fullname(), Node() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    QString name = stList.first().object().literal().toString();
+    bool isClark = ( name == QLatin1String("Clark Kent") );
+    bool isSuperMan = ( name == QLatin1String("Superman") );
+
+    QVERIFY( isClark || isSuperMan );
+}
+
+void DataManagementModelTest::testStoreResources_graphMetadataFail()
+{
+    QList<Soprano::Statement> stList = m_model->listStatements().allStatements();
+
+    QHash<QUrl, QVariant> additionalMetadata;
+    additionalMetadata.insert( NCO::fullname(), QLatin1String("graphs can't have names") );
+
+    SimpleResource res;
+    res.addType( NCO::Contact() );
+    res.addProperty( NCO::fullname(), QLatin1String("Harry Potter") );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("testApp"),
+                               IdentifyNew, NoStoreResourcesFlags, additionalMetadata );
+
+    // There should be an error as graphs cannot have NFO::FileDataObject
+    QVERIFY( m_dmModel->lastError() );
+
+    // Nothing should have changed
+    QList<Soprano::Statement> newStList = m_model->listStatements().allStatements();
+    QCOMPARE( stList, newStList );
+}
+
+void DataManagementModelTest::testStoreResources_randomNepomukUri()
+{
+    SimpleResource res(QUrl("nepomuk:/res/random-uri"));
+    res.addType( NCO::Contact() );
+    res.addProperty( NCO::fullname(), QLatin1String("Mickey Mouse") );
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("testApp") );
+
+    // There should be an error - We do not allow creation of arbitrary uris
+    // All uris must be created by the DataManagementModel
+    QVERIFY( m_dmModel->lastError() );
+}
+
+
+void DataManagementModelTest::testMergeResources()
+{
+    // first we need to create the two resources we want to merge as well as one that should not be touched
+    // for this simple test we put everything into one graph
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+
+    // the resource in which we want to merge
+    QUrl resA("nepomuk:/res/A");
+    m_model->addStatement(resA, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resA, QUrl("prop:/int_c1"), LiteralValue(42), g1);
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    // the resource that is going to be merged
+    // one duplicate property and one that differs, one backlink to ignore,
+    // one property with cardinality 1 to ignore
+    QUrl resB("nepomuk:/res/B");
+    m_model->addStatement(resB, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resB, QUrl("prop:/int_c1"), LiteralValue(12), g1);
+    m_model->addStatement(resB, QUrl("prop:/string"), LiteralValue(QLatin1String("hello")), g1);
+    m_model->addStatement(resA, QUrl("prop:/res"), resB, g1);
+
+    // resource C to ignore (except the backlink which needs to be updated)
+    QUrl resC("nepomuk:/res/C");
+    m_model->addStatement(resC, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resC, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+    m_model->addStatement(resC, QUrl("prop:/res"), resB, g1);
+
+
+    // now merge the resources
+    m_dmModel->mergeResources(resA, resB, QLatin1String("A"));
+
+    // make sure B is gone
+    QVERIFY(!m_model->containsAnyStatement(resB, Node(), Node()));
+    QVERIFY(!m_model->containsAnyStatement(Node(), Node(), resB));
+
+    // make sure A has all the required properties
+    QVERIFY(m_model->containsAnyStatement(resA, QUrl("prop:/int"), LiteralValue(42)));
+    QVERIFY(m_model->containsAnyStatement(resA, QUrl("prop:/int_c1"), LiteralValue(42)));
+    QVERIFY(m_model->containsAnyStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+    QVERIFY(m_model->containsAnyStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("hello"))));
+
+    // make sure A has no superfluous properties
+    QVERIFY(!m_model->containsAnyStatement(resA, QUrl("prop:/int_c1"), LiteralValue(12)));
+    QCOMPARE(m_model->listStatements(resA, QUrl("prop:/int"), Node()).allElements().count(), 1);
+
+    // make sure the backlink was updated
+    QVERIFY(m_model->containsAnyStatement(resC, QUrl("prop:/res"), resA));
+
+    // make sure C was not touched apart from the backlink
+    QVERIFY(m_model->containsStatement(resC, QUrl("prop:/int"), LiteralValue(42), g1));
+    QVERIFY(m_model->containsStatement(resC, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1));
+
+    QVERIFY(!haveTrailingGraphs());
+}
+
+void DataManagementModelTest::testMergeResources_protectedTypes()
+{
+    // create one resource to be merged with something else
+    QUrl mg1;
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase(), &mg1);
+
+    QUrl resA("res:/A");
+    m_model->addStatement(resA, RDF::type(), NAO::Tag(), g1);
+    m_model->addStatement(resA, QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(resA, QUrl("prop:/string"), LiteralValue(QLatin1String("Foobar")), g1);
+    m_model->addStatement(resA, NAO::created(), LiteralValue(QDateTime::currentDateTime()), g1);
+
+
+    // remember current state to compare later on
+    Soprano::Graph existingStatements = m_model->listStatements().allStatements();
+
+
+    // property 1
+    m_dmModel->mergeResources(resA, QUrl("prop:/int"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // property 2
+    m_dmModel->mergeResources(QUrl("prop:/int"), resA, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // class 1
+    m_dmModel->mergeResources(resA, NRL::Graph(), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // property 2
+    m_dmModel->mergeResources(NRL::Graph(), resA, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // graph 1
+    m_dmModel->mergeResources(resA, QUrl("graph:/onto"), QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+
+
+    // graph 2
+    m_dmModel->mergeResources(QUrl("graph:/onto"), resA, QLatin1String("testapp"));
+
+    // this call should fail
+    QVERIFY(m_dmModel->lastError());
+
+    // no data should have been changed
+    QCOMPARE(Graph(m_model->listStatements().allStatements()), existingStatements);
+}
+
+void DataManagementModelTest::testDescribeResources()
+{
+    QTemporaryFile fileC;
+    fileC.open();
+
+    // create some resources
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+
+    m_model->addStatement(QUrl("res:/A"), RDF::type(), NAO::Tag(), g1);
+    m_model->addStatement(QUrl("res:/A"), QUrl("prop:/res"), QUrl("res:/B"), g1);
+    m_model->addStatement(QUrl("res:/A"), NAO::hasSubResource(), QUrl("res:/B"), g1);
+
+    m_model->addStatement(QUrl("res:/B"), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")), g1);
+
+    m_model->addStatement(QUrl("res:/C"), NIE::url(), QUrl::fromLocalFile(fileC.fileName()), g1);
+    m_model->addStatement(QUrl("res:/C"), QUrl("prop:/int"), LiteralValue(42), g1);
+    m_model->addStatement(QUrl("res:/C"), NAO::hasSubResource(), QUrl("res:/D"), g1);
+
+    m_model->addStatement(QUrl("res:/D"), QUrl("prop:/string"), LiteralValue(QLatin1String("Hello")), g1);
+
+
+    // get one resource without sub-res
+    QList<SimpleResource> g = m_dmModel->describeResources(QList<QUrl>() << QUrl("res:/A"), false).toList();
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // only one resource in the result
+    QCOMPARE(g.count(), 1);
+
+    // the one result is res:/A
+    QCOMPARE(g.first().uri(), QUrl("res:/A"));
+
+    // res:/A has 3 properties
+    QCOMPARE(g.first().properties().count(), 3);
+
+
+    // get one resource by file-url without sub-res
+    g = m_dmModel->describeResources(QList<QUrl>() << QUrl::fromLocalFile(fileC.fileName()), false).toList();
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // only one resource in the result
+    QCOMPARE(g.count(), 1);
+
+    // the one result is res:/C
+    QCOMPARE(g.first().uri(), QUrl("res:/C"));
+
+    // res:/C has 3 properties
+    QCOMPARE(g.first().properties().count(), 3);
+
+
+    // get one resource with sub-res
+    g = m_dmModel->describeResources(QList<QUrl>() << QUrl("res:/A"), true).toList();
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // only one resource in the result
+    QCOMPARE(g.count(), 2);
+
+    // the results are res:/A and res:/B
+    SimpleResource r1 = g.first();
+    SimpleResource r2 = g.back();
+    QVERIFY(r1.uri() == QUrl("res:/A") || r2.uri() == QUrl("res:/A"));
+    QVERIFY(r1.uri() == QUrl("res:/B") || r2.uri() == QUrl("res:/B"));
+
+    // res:/A has 3 properties
+    if(r1.uri() == QUrl("res:/A")) {
+        QCOMPARE(r1.properties().count(), 3);
+        QCOMPARE(r2.properties().count(), 1);
+    }
+    else {
+        QCOMPARE(r1.properties().count(), 1);
+        QCOMPARE(r2.properties().count(), 3);
+    }
+
+
+    // get one resource via file URL with sub-res
+    g = m_dmModel->describeResources(QList<QUrl>() << QUrl::fromLocalFile(fileC.fileName()), true).toList();
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // only one resource in the result
+    QCOMPARE(g.count(), 2);
+
+    // the results are res:/C and res:/D
+    r1 = g.first();
+    r2 = g.back();
+    QVERIFY(r1.uri() == QUrl("res:/C") || r2.uri() == QUrl("res:/C"));
+    QVERIFY(r1.uri() == QUrl("res:/D") || r2.uri() == QUrl("res:/D"));
+
+    // res:/A has 3 properties
+    if(r1.uri() == QUrl("res:/C")) {
+        QCOMPARE(r1.properties().count(), 3);
+        QCOMPARE(r2.properties().count(), 1);
+    }
+    else {
+        QCOMPARE(r1.properties().count(), 1);
+        QCOMPARE(r2.properties().count(), 3);
+    }
+
+
+    // get two resources without sub-res
+    g = m_dmModel->describeResources(QList<QUrl>() << QUrl("res:/A") << QUrl("res:/C"), false).toList();
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // only one resource in the result
+    QCOMPARE(g.count(), 2);
+
+    // the results are res:/A and res:/C
+    r1 = g.first();
+    r2 = g.back();
+    QVERIFY(r1.uri() == QUrl("res:/A") || r2.uri() == QUrl("res:/A"));
+    QVERIFY(r1.uri() == QUrl("res:/C") || r2.uri() == QUrl("res:/C"));
+
+    // res:/A has 3 properties
+    QCOMPARE(r1.properties().count(), 3);
+    QCOMPARE(r2.properties().count(), 3);
+
+
+    // get two resources with sub-res
+    g = m_dmModel->describeResources(QList<QUrl>() << QUrl("res:/A") << QUrl("res:/C"), true).toList();
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // only one resource in the result
+    QCOMPARE(g.count(), 4);
+
+    // the results are res:/A, res:/B, res:/C and res:/D
+    QList<SimpleResource>::const_iterator it = g.constBegin();
+    r1 = *it;
+    ++it;
+    r2 = *it;
+    ++it;
+    SimpleResource r3 = *it;
+    ++it;
+    SimpleResource r4 = *it;
+    QVERIFY(r1.uri() == QUrl("res:/A") || r2.uri() == QUrl("res:/A") || r3.uri() == QUrl("res:/A") || r4.uri() == QUrl("res:/A"));
+    QVERIFY(r1.uri() == QUrl("res:/B") || r2.uri() == QUrl("res:/B") || r3.uri() == QUrl("res:/B") || r4.uri() == QUrl("res:/B"));
+    QVERIFY(r1.uri() == QUrl("res:/C") || r2.uri() == QUrl("res:/C") || r3.uri() == QUrl("res:/C") || r4.uri() == QUrl("res:/C"));
+    QVERIFY(r1.uri() == QUrl("res:/D") || r2.uri() == QUrl("res:/D") || r3.uri() == QUrl("res:/D") || r4.uri() == QUrl("res:/D"));
+
+
+    // get two resources with sub-res and mixed URL/URI
+    g = m_dmModel->describeResources(QList<QUrl>() << QUrl("res:/A") << QUrl::fromLocalFile(fileC.fileName()), true).toList();
+
+    // no error
+    QVERIFY(!m_dmModel->lastError());
+
+    // only one resource in the result
+    QCOMPARE(g.count(), 4);
+
+    // the results are res:/A, res:/B, res:/C and res:/D
+    it = g.constBegin();
+    r1 = *it;
+    ++it;
+    r2 = *it;
+    ++it;
+    r3 = *it;
+    ++it;
+    r4 = *it;
+    QVERIFY(r1.uri() == QUrl("res:/A") || r2.uri() == QUrl("res:/A") || r3.uri() == QUrl("res:/A") || r4.uri() == QUrl("res:/A"));
+    QVERIFY(r1.uri() == QUrl("res:/B") || r2.uri() == QUrl("res:/B") || r3.uri() == QUrl("res:/B") || r4.uri() == QUrl("res:/B"));
+    QVERIFY(r1.uri() == QUrl("res:/C") || r2.uri() == QUrl("res:/C") || r3.uri() == QUrl("res:/C") || r4.uri() == QUrl("res:/C"));
+    QVERIFY(r1.uri() == QUrl("res:/D") || r2.uri() == QUrl("res:/D") || r3.uri() == QUrl("res:/D") || r4.uri() == QUrl("res:/D"));
+}
+
+KTempDir * DataManagementModelTest::createNieUrlTestData()
+{
+    // now we create a real example with some real files:
+    // mainDir
+    // |- dir1
+    //    |- dir11
+    //       |- file111
+    //    |- dir12
+    //       |- dir121
+    //          |- file1211
+    //    |- file11
+    //    |- dir13
+    // |- dir2
+    KTempDir* mainDir = new KTempDir();
+    QDir dir(mainDir->name());
+    dir.mkdir(QLatin1String("dir1"));
+    dir.mkdir(QLatin1String("dir2"));
+    dir.cd(QLatin1String("dir1"));
+    dir.mkdir(QLatin1String("dir11"));
+    dir.mkdir(QLatin1String("dir12"));
+    dir.mkdir(QLatin1String("dir13"));
+    QFile file(dir.filePath(QLatin1String("file11")));
+    file.open(QIODevice::WriteOnly);
+    file.close();
+    dir.cd(QLatin1String("dir12"));
+    dir.mkdir(QLatin1String("dir121"));
+    dir.cd(QLatin1String("dir121"));
+    file.setFileName(dir.filePath(QLatin1String("file1211")));
+    file.open(QIODevice::WriteOnly);
+    file.close();
+    dir.cdUp();
+    dir.cdUp();
+    dir.cd(QLatin1String("dir11"));
+    file.setFileName(dir.filePath(QLatin1String("file111")));
+    file.open(QIODevice::WriteOnly);
+    file.close();
+
+    // We now create the situation in the model
+    // for that we use 2 graphs
+    const QUrl g1 = m_nrlModel->createGraph(NRL::InstanceBase());
+    const QUrl g2 = m_nrlModel->createGraph(NRL::InstanceBase());
+    const QString basePath = mainDir->name();
+
+    // nie:url properties for all of them (spread over both graphs)
+    m_model->addStatement(QUrl("res:/dir1"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir1")), g1);
+    m_model->addStatement(QUrl("res:/dir2"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir2")), g2);
+    m_model->addStatement(QUrl("res:/dir11"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir1/dir11")), g1);
+    m_model->addStatement(QUrl("res:/dir12"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir1/dir12")), g2);
+    m_model->addStatement(QUrl("res:/dir13"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir1/dir13")), g1);
+    m_model->addStatement(QUrl("res:/file11"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir1/file11")), g2);
+    m_model->addStatement(QUrl("res:/file111"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir1/dir11/file111")), g1);
+    m_model->addStatement(QUrl("res:/dir121"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir2/dir121")), g2);
+    m_model->addStatement(QUrl("res:/file1211"), NIE::url(), QUrl(QLatin1String("file://") + basePath + QLatin1String("dir2/dir121/file1211")), g1);
+
+    // we define filename and parent folder only for some to test if the optional clause in the used query works properly
+    m_model->addStatement(QUrl("res:/dir1"), NFO::fileName(), LiteralValue(QLatin1String("dir1")), g1);
+    m_model->addStatement(QUrl("res:/dir2"), NFO::fileName(), LiteralValue(QLatin1String("dir2")), g1);
+    m_model->addStatement(QUrl("res:/dir11"), NFO::fileName(), LiteralValue(QLatin1String("dir11")), g2);
+    m_model->addStatement(QUrl("res:/dir12"), NFO::fileName(), LiteralValue(QLatin1String("dir12")), g2);
+    m_model->addStatement(QUrl("res:/file11"), NFO::fileName(), LiteralValue(QLatin1String("file11")), g1);
+    m_model->addStatement(QUrl("res:/file111"), NFO::fileName(), LiteralValue(QLatin1String("file111")), g2);
+    m_model->addStatement(QUrl("res:/dir121"), NFO::fileName(), LiteralValue(QLatin1String("dir121")), g2);
+
+    m_model->addStatement(QUrl("res:/dir11"), NIE::isPartOf(), QUrl("res:/dir1"), g1);
+    m_model->addStatement(QUrl("res:/dir12"), NIE::isPartOf(), QUrl(QLatin1String("res:/dir1")), g2);
+    m_model->addStatement(QUrl("res:/dir13"), NIE::isPartOf(), QUrl(QLatin1String("res:/dir1")), g1);
+    m_model->addStatement(QUrl("res:/file111"), NIE::isPartOf(), QUrl(QLatin1String("res:/dir11")), g1);
+    m_model->addStatement(QUrl("res:/dir121"), NIE::isPartOf(), QUrl(QLatin1String("res:/dir2")), g2);
+    m_model->addStatement(QUrl("res:/file1211"), NIE::isPartOf(), QUrl(QLatin1String("res:/dir121")), g1);
+
+    return mainDir;
+}
+
+bool DataManagementModelTest::haveTrailingGraphs() const
+{
+    return m_model->executeQuery(QString::fromLatin1("ask where { "
+                                                     "?g a ?t . "
+                                                     "FILTER(!bif:exists( (select (1) where { graph ?g { ?s ?p ?o . } . }))) . "
+                                                     "FILTER(?t in (%1,%2,%3)) . "
+                                                     "}")
+                                 .arg(Node::resourceToN3(NRL::InstanceBase()),
+                                      Node::resourceToN3(NRL::DiscardableInstanceBase()),
+                                      Node::resourceToN3(NRL::GraphMetadata())),
+                                 Soprano::Query::QueryLanguageSparql).boolValue();
+}
+
+void DataManagementModelTest::testImportResources()
+{
+    // create the test data
+    QTemporaryFile fileA;
+    fileA.open();
+
+    Soprano::Graph graph;
+    graph.addStatement(Node(QString::fromLatin1("res1")), QUrl("prop:/int"), LiteralValue(42));
+    graph.addStatement(Node(QString::fromLatin1("res1")), RDF::type(), QUrl("class:/typeA"));
+    graph.addStatement(Node(QString::fromLatin1("res1")), QUrl("prop:/res"), Node(QString::fromLatin1("res2")));
+    graph.addStatement(Node(QString::fromLatin1("res2")), RDF::type(), QUrl("class:/typeB"));
+    graph.addStatement(QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/int"), LiteralValue(12));
+    graph.addStatement(QUrl::fromLocalFile(fileA.fileName()), QUrl("prop:/string"), LiteralValue(QLatin1String("foobar")));
+
+    // write the test file
+    QTemporaryFile tmp;
+    tmp.open();
+    QTextStream str(&tmp);
+    Q_FOREACH(const Statement& s, graph.toList()) {
+        str << s.subject().toN3() << " " << s.predicate().toN3() << " " << s.object().toN3() << " ." << endl;
+    }
+    tmp.close();
+
+
+    // import the file
+    m_dmModel->importResources(QUrl::fromLocalFile(tmp.fileName()), QLatin1String("A"), Soprano::SerializationNTriples);
+
+
+    // make sure the data has been imported properly
+    QVERIFY(m_model->containsAnyStatement(Node(), QUrl("prop:/int"), LiteralValue(42)));
+    const QUrl res1Uri = m_model->listStatements(Node(), QUrl("prop:/int"), LiteralValue(42)).allStatements().first().subject().uri();
+    QVERIFY(m_model->containsAnyStatement(res1Uri, RDF::type(), QUrl("class:/typeA")));
+    QVERIFY(m_model->containsAnyStatement(res1Uri, QUrl("prop:/res"), Node()));
+    const QUrl res2Uri = m_model->listStatements(res1Uri, QUrl("prop:/res"), Node()).allStatements().first().object().uri();
+    QVERIFY(m_model->containsAnyStatement(res2Uri, RDF::type(), QUrl("class:/typeB")));
+    QVERIFY(m_model->containsAnyStatement(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())));
+    const QUrl res3Uri = m_model->listStatements(Node(), NIE::url(), QUrl::fromLocalFile(fileA.fileName())).allStatements().first().subject().uri();
+    QVERIFY(m_model->containsAnyStatement(res3Uri, QUrl("prop:/int"), LiteralValue(12)));
+    QVERIFY(m_model->containsAnyStatement(res3Uri, QUrl("prop:/string"), LiteralValue(QLatin1String("foobar"))));
+
+    // make sure the metadata is there
+    QVERIFY(m_model->containsAnyStatement(res1Uri, NAO::lastModified(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res1Uri, NAO::created(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res2Uri, NAO::lastModified(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res2Uri, NAO::created(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res3Uri, NAO::lastModified(), Node()));
+    QVERIFY(m_model->containsAnyStatement(res3Uri, NAO::created(), Node()));
+}
+
+QTEST_KDEMAIN_CORE(DataManagementModelTest)
+
+#include "datamanagementmodeltest.moc"
diff --git a/nepomuk/services/storage/test/datamanagementmodeltest.h b/nepomuk/services/storage/test/datamanagementmodeltest.h
new file mode 100644
index 0000000..14b70ea
--- /dev/null
+++ b/nepomuk/services/storage/test/datamanagementmodeltest.h
@@ -0,0 +1,171 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DATAMANAGEMENTMODELTEST_H
+#define DATAMANAGEMENTMODELTEST_H
+
+#include <QObject>
+
+namespace Soprano {
+class Model;
+class NRLModel;
+}
+namespace Nepomuk {
+class DataManagementModel;
+class ClassAndPropertyTree;
+}
+class KTempDir;
+
+class DataManagementModelTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+
+    void testAddProperty();
+    void testAddProperty_createRes();
+    void testAddProperty_cardinality();
+    void testAddProperty_file();
+    void testAddProperty_invalidFile();
+    void testAddProperty_invalid_args();
+    void testAddProperty_protectedTypes();
+    void testAddProperty_akonadi();
+
+    void testSetProperty();
+    void testSetProperty_createRes();
+    void testSetProperty_overwrite();
+    void testSetProperty_invalid_args();
+    void testSetProperty_nieUrl1();
+    void testSetProperty_nieUrl2();
+    void testSetProperty_nieUrl3();
+    void testSetProperty_nieUrl4();
+    void testSetProperty_nieUrl5();
+    void testSetProperty_nieUrl6();
+    void testSetProperty_protectedTypes();
+    void testSetProperty_legacyData();
+
+    void testRemoveProperty();
+    void testRemoveProperty_file();
+    void testRemoveProperty_invalid_args();
+    void testRemoveProperty_protectedTypes();
+
+    void testRemoveProperties();
+    void testRemoveProperties_invalid_args();
+    void testRemoveProperties_protectedTypes();
+
+    void testRemoveResources();
+    void testRemoveResources_subresources();
+    void testRemoveResources_invalid_args();
+    void testRemoveResources_protectedTypes();
+    void testRemoveResources_mtimeRelated();
+    void testRemoveResources_deletedFile();
+
+    void testCreateResource();
+    void testCreateResource_invalid_args();
+
+    void testRemoveDataByApplication1();
+    void testRemoveDataByApplication2();
+    void testRemoveDataByApplication3();
+    void testRemoveDataByApplication4();
+    void testRemoveDataByApplication5();
+    void testRemoveDataByApplication6();
+    void testRemoveDataByApplication7();
+    void testRemoveDataByApplication8();
+    void testRemoveDataByApplication9();
+    void testRemoveDataByApplication10();
+    void testRemoveDataByApplication11();
+    void testRemoveDataByApplication_subResourcesOfSubResources();
+    void testRemoveDataByApplication_realLife();
+    void testRemoveDataByApplication_nieUrl();
+    void testRemoveDataByApplication_nieUrlRelated();
+    void testRemoveDataByApplication_mtime();
+    void testRemoveDataByApplication_mtimeRelated();
+    void testRemoveDataByApplication_related();
+    void testRemoveDataByApplication_legacyIndexerData();
+    void testRemoveDataByApplication_deletedFile();
+
+    void testRemoveAllDataByApplication1();
+    void testRemoveAllDataByApplication2();
+    void testRemoveAllDataByApplication3();
+    void testRemoveAllDataByApplication4();
+
+    void testStoreResources_strigiCase();
+    void testStoreResources_graphRules();
+    void testStoreResources_createResource();
+    void testStoreResources_invalid_args();
+    void testStoreResources_invalid_args_with_existing();
+    void testStoreResources_file1();
+    void testStoreResources_file2();
+    void testStoreResources_file3();
+    void testStoreResources_file4();
+    void testStoreResources_folder();
+    void testStoreResources_fileExists();
+    void testStoreResources_sameNieUrl();
+    void testStoreResources_metadata();
+    void testStoreResources_protectedTypes();
+    void testStoreResources_superTypes();
+    void testStoreResources_missingMetadata();
+    void testStoreResources_multiMerge();
+    void testStoreResources_realLife();
+    void testStoreResources_trivialMerge();
+    void testStoreResources_noTypeMatch1();
+    void testStoreResources_noTypeMatch2();
+    void testStoreResources_faultyMetadata();
+    void testStoreResources_additionalMetadataApp();
+    void testStoreResources_itemUris();
+    void testStoreResources_kioProtocols();
+    void testStoreResources_duplicates();
+    void testStoreResources_overwriteProperties();
+    void testStoreResources_correctDomainInStore();
+    void testStoreResources_correctDomainInStore2();
+    void testStoreResources_correctRangeInStore();
+    void testStoreResources_correctRangeInStore2();
+    void testStoreResources_duplicateValuesAsString();
+    void testStoreResources_ontology();
+    void testStoreResources_legacyUris();
+    void testStoreResources_lazyCardinalities();
+    void testStoreResources_graphMetadataFail();
+    void testStoreResources_randomNepomukUri();
+
+    void testMergeResources();
+    void testMergeResources_protectedTypes();
+
+    void testDescribeResources();
+
+    void testImportResources();
+
+private:
+    KTempDir* createNieUrlTestData();
+
+    void resetModel();
+    bool haveTrailingGraphs() const;
+
+    KTempDir* m_storageDir;
+    Soprano::Model* m_model;
+    Soprano::NRLModel* m_nrlModel;
+    Nepomuk::ClassAndPropertyTree* m_classAndPropertyTree;
+    Nepomuk::DataManagementModel* m_dmModel;
+};
+
+#endif
diff --git a/nepomuk/services/storage/test/fakedatamanagementservice.cpp b/nepomuk/services/storage/test/fakedatamanagementservice.cpp
new file mode 100644
index 0000000..40140be
--- /dev/null
+++ b/nepomuk/services/storage/test/fakedatamanagementservice.cpp
@@ -0,0 +1,147 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "fakedatamanagementservice.h"
+#include "../datamanagementmodel.h"
+#include "../datamanagementadaptor.h"
+#include "../classandpropertytree.h"
+
+#include <Soprano/Soprano>
+#include <Soprano/Server/DBusExportModel>
+#include <Soprano/Graph>
+#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
+#include <Soprano/NRLModel>
+
+#include <ktempdir.h>
+#include <KDebug>
+
+#include <KApplication>
+#include <KAboutData>
+#include <KCmdLineArgs>
+#include <KCmdLineOptions>
+
+#include <QtDBus>
+
+#include <signal.h>
+#include <stdio.h>
+
+using namespace Soprano;
+using namespace Nepomuk;
+
+namespace {
+#ifndef Q_OS_WIN
+    void signalHandler( int signal )
+    {
+        switch( signal ) {
+        case SIGHUP:
+        case SIGQUIT:
+        case SIGINT:
+            QCoreApplication::exit( 0 );
+        }
+    }
+#endif
+
+    void installSignalHandler() {
+#ifndef Q_OS_WIN
+        struct sigaction sa;
+        ::memset( &sa, 0, sizeof( sa ) );
+        sa.sa_handler = signalHandler;
+        sigaction( SIGHUP, &sa, 0 );
+        sigaction( SIGINT, &sa, 0 );
+        sigaction( SIGQUIT, &sa, 0 );
+#endif
+    }
+}
+
+FakeDataManagementService::FakeDataManagementService(QObject *parent)
+    : QObject(parent)
+{
+    // create our fake storage
+    const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByName( "virtuosobackend" );
+    Q_ASSERT(backend);
+    m_storageDir = new KTempDir();
+    m_model = backend->createModel( Soprano::BackendSettings() << Soprano::BackendSetting(Soprano::BackendOptionStorageDir, m_storageDir->name()) );
+    Q_ASSERT(m_model);
+
+    // create the data management service stack connected to the fake storage
+    m_nrlModel = new Soprano::NRLModel(m_model);
+    m_classAndPropertyTree = new Nepomuk::ClassAndPropertyTree(this);
+    m_dmModel = new Nepomuk::DataManagementModel(m_classAndPropertyTree, m_nrlModel);
+    m_dmAdaptor = new Nepomuk::DataManagementAdaptor(m_dmModel);
+
+    // register the adaptor
+    QDBusConnection::sessionBus().registerObject(QLatin1String("/datamanagement"), m_dmAdaptor, QDBusConnection::ExportScriptableContents);
+
+    // register the dm model itself - simply to let the test case have access to the updateTypeCachesAndSoOn() method
+    QDBusConnection::sessionBus().registerObject(QLatin1String("/fakedms"), this, QDBusConnection::ExportAllSlots);
+
+    // register the service itself
+    QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.nepomuk.FakeDataManagement"));
+
+    // register our base model via dbus so the test case can access it
+    Soprano::Server::DBusExportModel* dbusModel = new Soprano::Server::DBusExportModel(m_model);
+    dbusModel->setParent(this);
+    dbusModel->registerModel(QLatin1String("/model"));
+}
+
+FakeDataManagementService::~FakeDataManagementService()
+{
+    delete m_dmAdaptor;
+    delete m_dmModel;
+    delete m_nrlModel;
+    delete m_model;
+    delete m_storageDir;
+}
+
+
+void FakeDataManagementService::updateClassAndPropertyTree()
+{
+    m_classAndPropertyTree->rebuildTree(m_model);
+}
+
+
+int main( int argc, char** argv )
+{
+    KAboutData aboutData( "fakedms", "fakedms",
+                          ki18n("Fake Data Management Service"),
+                          "0.1",
+                          ki18n("Fake Data Management Service"),
+                          KAboutData::License_GPL,
+                          ki18n("(c) 2011, Sebastian Trüg"),
+                          KLocalizedString(),
+                          "http://nepomuk.kde.org" );
+    aboutData.setProgramIconName( "nepomuk" );
+    aboutData.addAuthor(ki18n("Sebastian Trüg"),ki18n("Maintainer"), "trueg@kde.org");
+
+    KCmdLineOptions options;
+    KCmdLineArgs::addCmdLineOptions( options );
+    KCmdLineArgs::init( argc, argv, &aboutData );
+
+    KApplication app( false );
+    app.disableSessionManagement();
+    installSignalHandler();
+    QApplication::setQuitOnLastWindowClosed( false );
+
+    FakeDataManagementService fs;
+    return app.exec();
+}
+
+#include "fakedatamanagementservice.moc"
diff --git a/nepomuk/services/storage/test/fakedatamanagementservice.h b/nepomuk/services/storage/test/fakedatamanagementservice.h
new file mode 100644
index 0000000..b462f0a
--- /dev/null
+++ b/nepomuk/services/storage/test/fakedatamanagementservice.h
@@ -0,0 +1,59 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FAKEDATAMANAGEMENTSERVICE_H
+#define FAKEDATAMANAGEMENTSERVICE_H
+
+#include <QObject>
+
+namespace Soprano {
+class Model;
+class NRLModel;
+}
+namespace Nepomuk {
+class DataManagementModel;
+class DataManagementAdaptor;
+class ClassAndPropertyTree;
+}
+class KTempDir;
+
+class FakeDataManagementService : public QObject
+{
+    Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", "org.kde.nepomuk.FakeDataManagement")
+
+public:
+    FakeDataManagementService(QObject *parent = 0);
+    ~FakeDataManagementService();
+
+public Q_SLOTS:
+    void updateClassAndPropertyTree();
+
+private:
+    KTempDir* m_storageDir;
+    Soprano::Model* m_model;
+    Soprano::NRLModel* m_nrlModel;
+    Nepomuk::ClassAndPropertyTree* m_classAndPropertyTree;
+    Nepomuk::DataManagementModel* m_dmModel;
+    Nepomuk::DataManagementAdaptor* m_dmAdaptor;
+};
+
+#endif
diff --git a/nepomuk/services/storage/test/nepomuk_dms_test_config.h.cmake b/nepomuk/services/storage/test/nepomuk_dms_test_config.h.cmake
new file mode 100644
index 0000000..e0e7b8d
--- /dev/null
+++ b/nepomuk/services/storage/test/nepomuk_dms_test_config.h.cmake
@@ -0,0 +1,27 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef NEPOMUK_DMS_TEST_CONFIG_H_CMAKE
+#define NEPOMUK_DMS_TEST_CONFIG_H_CMAKE
+
+#define FAKEDMS_BIN "${EXECUTABLE_OUTPUT_PATH}/fakedms"
+
+#endif // NEPOMUK_DMS_TEST_CONFIG_H_CMAKE
diff --git a/nepomuk/services/storage/test/qtest_dms.cpp b/nepomuk/services/storage/test/qtest_dms.cpp
new file mode 100644
index 0000000..57ec301
--- /dev/null
+++ b/nepomuk/services/storage/test/qtest_dms.cpp
@@ -0,0 +1,176 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "qtest_dms.h"
+
+#include <Soprano/Vocabulary/RDF>
+#include <Soprano/Vocabulary/RDFS>
+#include <Soprano/Vocabulary/NAO>
+#include <Soprano/Vocabulary/NRL>
+#include <Soprano/Vocabulary/XMLSchema>
+#include <Nepomuk/Vocabulary/NFO>
+#include <Nepomuk/Vocabulary/NMM>
+#include <Nepomuk/Vocabulary/NCO>
+#include <Nepomuk/Vocabulary/NIE>
+
+#include <Soprano/LiteralValue>
+
+using namespace Soprano::Vocabulary;
+using namespace Soprano;
+using namespace Nepomuk::Vocabulary;
+using namespace Nepomuk;
+
+void Nepomuk::insertOntologies(Soprano::Model* m_model, const QUrl& graph)
+{
+    m_model->addStatement( graph, RDF::type(), NRL::Ontology(), graph );
+    // removeResources depends on type inference
+    m_model->addStatement( graph, RDF::type(), NRL::Graph(), graph );
+
+    m_model->addStatement( QUrl("prop:/int"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/int"), RDFS::range(), XMLSchema::xsdInt(), graph );
+
+    m_model->addStatement( QUrl("prop:/int2"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/int2"), RDFS::range(), XMLSchema::xsdInt(), graph );
+
+    m_model->addStatement( QUrl("prop:/int3"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/int3"), RDFS::range(), XMLSchema::xsdInt(), graph );
+
+    m_model->addStatement( QUrl("prop:/int_c1"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/int_c1"), RDFS::range(), XMLSchema::xsdInt(), graph );
+    m_model->addStatement( QUrl("prop:/int_c1"), NRL::maxCardinality(), LiteralValue(1), graph );
+
+    m_model->addStatement( QUrl("prop:/string"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/string"), RDFS::range(), XMLSchema::string(), graph );
+
+    m_model->addStatement( QUrl("prop:/res"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/res"), RDFS::range(), RDFS::Resource(), graph );
+
+    m_model->addStatement( QUrl("prop:/res2"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/res2"), RDFS::range(), RDFS::Resource(), graph );
+
+    m_model->addStatement( QUrl("prop:/res3"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/res3"), RDFS::range(), RDFS::Resource(), graph );
+
+    m_model->addStatement( QUrl("prop:/res_c1"), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( QUrl("prop:/res_c1"), RDFS::range(), RDFS::Resource(), graph );
+    m_model->addStatement( QUrl("prop:/res_c1"), NRL::maxCardinality(), LiteralValue(1), graph );
+
+    m_model->addStatement( QUrl("class:/typeA"), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( QUrl("class:/typeB"), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( QUrl("class:/typeB"), RDFS::subClassOf(), QUrl("class:/typeA"), graph );
+
+    // properties used all the time
+    m_model->addStatement( NAO::identifier(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( RDF::type(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( RDF::type(), RDFS::range(), RDFS::Class(), graph );
+    m_model->addStatement( NIE::url(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NIE::url(), RDFS::range(), RDFS::Resource(), graph );
+
+
+    // some ontology things the ResourceMerger depends on
+    m_model->addStatement( RDFS::Class(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( RDFS::Class(), RDFS::subClassOf(), RDFS::Resource(), graph );
+    m_model->addStatement( NRL::Graph(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NRL::InstanceBase(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NRL::InstanceBase(), RDFS::subClassOf(), NRL::Graph(), graph );
+    m_model->addStatement( NAO::prefLabel(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NAO::prefLabel(), RDFS::range(), RDFS::Literal(), graph );
+    m_model->addStatement( NFO::fileName(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NFO::fileName(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NCO::fullname(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NCO::fullname(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NIE::title(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NIE::title(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NAO::created(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NAO::created(), RDFS::range(), XMLSchema::dateTime(), graph );
+    m_model->addStatement( NAO::created(), NRL::maxCardinality(), LiteralValue(1), graph );
+    m_model->addStatement( NAO::lastModified(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NAO::lastModified(), RDFS::range(), XMLSchema::dateTime(), graph );
+    m_model->addStatement( NAO::lastModified(), NRL::maxCardinality(), LiteralValue(1), graph );
+
+    // used in testStoreResources_sameNieUrl
+    m_model->addStatement( NAO::numericRating(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NAO::numericRating(), RDFS::range(), XMLSchema::xsdInt(), graph );
+    m_model->addStatement( NAO::numericRating(), NRL::maxCardinality(), LiteralValue(1), graph );
+
+    // some ontology things we need in testStoreResources_realLife
+    m_model->addStatement( NMM::season(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NMM::season(), RDFS::range(), XMLSchema::xsdInt(), graph );
+    m_model->addStatement( NMM::episodeNumber(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NMM::episodeNumber(), RDFS::range(), XMLSchema::xsdInt(), graph );
+    m_model->addStatement( NMM::hasEpisode(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NMM::hasEpisode(), RDFS::range(), NMM::TVShow(), graph );
+    m_model->addStatement( NIE::description(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NIE::description(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NMM::synopsis(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NMM::synopsis(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NMM::series(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NMM::series(), RDFS::range(), NMM::TVSeries(), graph );
+    m_model->addStatement( NIE::title(), RDFS::subClassOf(), QUrl("http://www.semanticdesktop.org/ontologies/2007/08/15/nao#identifyingProperty"), graph );
+
+    // some ontology things we need in testStoreResources_strigiCase
+    m_model->addStatement( NMM::performer(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NMM::performer(), RDFS::domain(), NMM::MusicPiece(), graph );
+    m_model->addStatement( NMM::performer(), RDFS::range(), NCO::Contact(), graph );
+    m_model->addStatement( NMM::musicAlbum(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NMM::musicAlbum(), RDFS::range(), NMM::MusicAlbum(), graph );
+    m_model->addStatement( NMM::MusicAlbum(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NMM::TVShow(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NMM::TVSeries(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NMM::MusicPiece(), RDF::type(), RDFS::Class(), graph );
+
+    // used by testStoreResources_duplicates
+    m_model->addStatement( NFO::hashAlgorithm(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NFO::hashAlgorithm(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NFO::hashValue(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NFO::hashValue(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NFO::hashValue(), NRL::maxCardinality(), LiteralValue(1), graph );
+    m_model->addStatement( NFO::hasHash(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NFO::hasHash(), RDFS::range(), NFO::FileHash(), graph );
+    m_model->addStatement( NFO::hasHash(), RDFS::domain(), NFO::FileDataObject(), graph );
+    m_model->addStatement( NFO::FileHash(), RDF::type(), RDFS::Resource(), graph );
+    m_model->addStatement( NFO::FileHash(), RDF::type(), RDFS::Class(), graph );
+
+
+    m_model->addStatement( NIE::isPartOf(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NIE::isPartOf(), RDFS::range(), NFO::FileDataObject(), graph );
+    m_model->addStatement( NIE::lastModified(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NIE::lastModified(), RDFS::range(), XMLSchema::dateTime(), graph );
+
+    m_model->addStatement( NCO::fullname(), RDF::type(), RDF::Property(), graph );
+    m_model->addStatement( NCO::fullname(), RDFS::range(), XMLSchema::string(), graph );
+    m_model->addStatement( NCO::fullname(), RDFS::domain(), NCO::Contact(), graph );
+    m_model->addStatement( NCO::fullname(), NRL::maxCardinality(), LiteralValue(1), graph );
+    m_model->addStatement( NCO::Contact(), RDF::type(), RDFS::Resource(), graph );
+    m_model->addStatement( NCO::Contact(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NCO::Contact(), RDFS::subClassOf(), NCO::Role(), graph );
+    m_model->addStatement( NCO::Contact(), RDFS::subClassOf(), NAO::Party(), graph );
+
+    m_model->addStatement( NAO::Tag(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NFO::FileDataObject(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NFO::Folder(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NFO::Video(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( NIE::InformationElement(), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( QUrl("class:/typeA"), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( QUrl("class:/typeB"), RDF::type(), RDFS::Class(), graph );
+    m_model->addStatement( QUrl("class:/typeC"), RDF::type(), RDFS::Class(), graph );
+}
diff --git a/nepomuk/services/storage/test/qtest_dms.h b/nepomuk/services/storage/test/qtest_dms.h
new file mode 100644
index 0000000..68c3a8f
--- /dev/null
+++ b/nepomuk/services/storage/test/qtest_dms.h
@@ -0,0 +1,49 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef QTEST_DMS_H
+#define QTEST_DMS_H
+
+#include <QtTest>
+#include <Soprano/Statement>
+#include <Soprano/Node>
+#include <Soprano/Model>
+
+namespace QTest {
+template<>
+inline char* toString(const Soprano::Node& node) {
+    return qstrdup( node.toN3().toLatin1().data() );
+}
+
+template<>
+inline char* toString(const Soprano::Statement& s) {
+    return qstrdup( (s.subject().toN3() + QLatin1String(" ") +
+                     s.predicate().toN3() + QLatin1String(" ") +
+                     s.object().toN3() + QLatin1String(" . ")).toLatin1().data() );
+}
+
+}
+
+namespace Nepomuk {
+    void insertOntologies( Soprano::Model *model, const QUrl &ontologyGraph );
+}
+
+#endif // QTEST_DMS_H
diff --git a/nepomuk/services/storage/test/removablemediamodeltest.cpp b/nepomuk/services/storage/test/removablemediamodeltest.cpp
new file mode 100644
index 0000000..8bf3ab1
--- /dev/null
+++ b/nepomuk/services/storage/test/removablemediamodeltest.cpp
@@ -0,0 +1,288 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "removablemediamodeltest.h"
+
+#define private public
+#include "../removablemediamodel.h"
+#undef private
+
+#include <QtTest>
+#include "qtest_kde.h"
+#include "qtest_dms.h"
+
+#include <QtDBus>
+#include <Soprano/Soprano>
+#include <KDebug>
+
+#include <Solid/DeviceNotifier>
+#include <Solid/DeviceInterface>
+#include <Solid/Block>
+#include <Solid/Device>
+#include <Solid/StorageDrive>
+#include <Solid/StorageVolume>
+#include <Solid/StorageAccess>
+#include <Solid/Predicate>
+
+#include <Nepomuk/Vocabulary/NIE>
+
+#ifndef FAKE_COMPUTER_XML
+    #error "FAKE_COMPUTER_XML not set. An XML file describing a computer is required for this test"
+#endif
+
+#define SOLID_FAKEHW_SERVICE QDBusConnection::sessionBus().baseService()
+#define SOLID_FAKEHW_PATH "/org/kde/solid/fakehw"
+#define SOLID_FAKEHW_INTERFACE "local.qttest.Solid.Backends.Fake.FakeManager"
+
+// TODO: also test mounting a different device to the same mount path
+
+using namespace Nepomuk;
+using namespace Nepomuk::Vocabulary;
+using namespace Soprano;
+
+Q_DECLARE_METATYPE(Soprano::Node)
+Q_DECLARE_METATYPE(Soprano::Statement)
+
+
+namespace {
+/// Plug a device in the fake Solid hw manager.
+void plugDevice(const QString& udi) {
+    QDBusInterface(SOLID_FAKEHW_SERVICE, SOLID_FAKEHW_PATH, SOLID_FAKEHW_INTERFACE, QDBusConnection::sessionBus())
+            .call(QLatin1String("plug"), udi);
+}
+
+/// Unplug a device in the fake Solid hw manager.
+void unplugDevice(const QString& udi) {
+    QDBusInterface(SOLID_FAKEHW_SERVICE, SOLID_FAKEHW_PATH, SOLID_FAKEHW_INTERFACE, QDBusConnection::sessionBus())
+            .call(QLatin1String("unplug"), udi);
+}
+
+const char* s_udiXyz123 = "/org/kde/solid/fakehw/volume_part1_size_993284096";
+}
+
+void RemovableMediaModelTest::initTestCase()
+{
+    // make sure Solid uses the fake manager
+    setenv("SOLID_FAKEHW", FAKE_COMPUTER_XML, 1);
+
+    // we simply need some memory model for now - nothing fancy
+    m_model = Soprano::createModel();
+    m_model->setParent(this);
+    m_rmModel = new RemovableMediaModel(m_model, this);
+}
+
+
+void RemovableMediaModelTest::testConvertFileUrlsInStatement_data()
+{
+    QTest::addColumn<Statement>( "original" );
+    QTest::addColumn<Statement>( "converted" );
+
+    const Statement randomStatement(QUrl("nepomuk:/res/xyz"), QUrl("onto:someProp"), LiteralValue("foobar"));
+    QTest::newRow("noFileUrls") << randomStatement << randomStatement;
+
+    const Statement randomFileSubject(QUrl("file:///tmp/test"), QUrl("onto:someProp"), LiteralValue("foobar"));
+    QTest::newRow("randomFileUrlInSubject") << randomFileSubject << randomFileSubject;
+
+    const Statement convertableFileSubject(QUrl("file:///media/XO-Y4/test.txt"), QUrl("onto:someProp"), LiteralValue("foobar"));
+    QTest::newRow("convertableFileUrlInSubject") << convertableFileSubject << convertableFileSubject;
+
+    const Statement convertableFileObjectWithoutNieUrl(QUrl("nepomuk:/res/xyz"), QUrl("onto:someProp"), QUrl("file:///media/XO-Y4/test.txt"));
+    QTest::newRow("convertableFileUrlInObjectWithoutNieUrl") << convertableFileObjectWithoutNieUrl << convertableFileObjectWithoutNieUrl;
+
+    const Statement convertableFileObjectWithNieUrl1_original(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("file:///media/XO-Y4/test.txt"));
+    const Statement convertableFileObjectWithNieUrl1_converted(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("filex://xyz-123/test.txt"));
+    QTest::newRow("convertableFileUrlInObjectWithNieUrl1") << convertableFileObjectWithNieUrl1_original << convertableFileObjectWithNieUrl1_converted;
+
+    const Statement convertableFileObjectWithNieUrl2_original(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("file:///media/XO-Y4"));
+    const Statement convertableFileObjectWithNieUrl2_converted(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("filex://xyz-123"));
+    QTest::newRow("convertableFileUrlInObjectWithNieUrl2") << convertableFileObjectWithNieUrl2_original << convertableFileObjectWithNieUrl2_converted;
+
+    const Statement convertableFileObjectWithNieUrl3_original(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("file:///media/nfs/test.txt"));
+    const Statement convertableFileObjectWithNieUrl3_converted(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("nfs://thehost/solid-path/test.txt"));
+    QTest::newRow("convertableFileUrlInObjectWithNieUrl3") << convertableFileObjectWithNieUrl3_original << convertableFileObjectWithNieUrl3_converted;
+
+    const Statement convertableFileObjectWithNieUrl4_original(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("file:///media/nfs"));
+    const Statement convertableFileObjectWithNieUrl4_converted(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("nfs://thehost/solid-path"));
+    QTest::newRow("convertableFileUrlInObjectWithNieUrl4") << convertableFileObjectWithNieUrl4_original << convertableFileObjectWithNieUrl4_converted;
+}
+
+
+void RemovableMediaModelTest::testConvertFileUrlsInStatement()
+{
+    QFETCH(Statement, original);
+    QFETCH(Statement, converted);
+
+    QCOMPARE(m_rmModel->convertFileUrls(original), converted);
+}
+
+void RemovableMediaModelTest::testConvertFileUrlsInQuery_data()
+{
+    QTest::addColumn<QString>( "original" );
+    QTest::addColumn<QString>( "converted" );
+
+    QString query = QString::fromLatin1("select ?r where { ?r ?p <file:///media/foobar/test.txt> . }");
+    QTest::newRow("queryWithNonConvertableFileUrl") << query << query;
+
+    QTest::newRow("queryWithConvertableFileUrl1")
+            << QString::fromLatin1("select ?r where { ?r ?p <file:///media/XO-Y4/test.txt> . }")
+            << QString::fromLatin1("select ?r where { ?r ?p <filex://xyz-123/test.txt> . }");
+
+    QTest::newRow("queryWithConvertableFileUrl2")
+            << QString::fromLatin1("select ?r where { ?r ?p <file:///media/nfs/test.txt> . }")
+            << QString::fromLatin1("select ?r where { ?r ?p <nfs://thehost/solid-path/test.txt> . }");
+
+    QTest::newRow("queryWithConvertableRegex1")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . FILTER(REGEX(?u, '^file:///media/XO-Y4/test')) . }")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . FILTER(REGEX(?u, '^filex://xyz-123/test')) . }");
+
+    QTest::newRow("queryWithConvertableRegex2")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . FILTER(REGEX(?u, '^file:///media/nfs/')) . }")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . FILTER(REGEX(?u, '^nfs://thehost/solid-path/')) . }");
+}
+
+void RemovableMediaModelTest::testConvertFileUrlsInQuery()
+{
+    QFETCH(QString, original);
+    QFETCH(QString, converted);
+
+    QCOMPARE(m_rmModel->convertFileUrls(original), converted);
+}
+
+void RemovableMediaModelTest::testConvertFilxUrl_data()
+{
+    QTest::addColumn<Node>( "original" );
+    QTest::addColumn<Node>( "converted" );
+
+    const Node nothingToConvertFilex(QUrl("filex://abc-789/hello/world"));
+    QTest::newRow("nothingToConvertFilex") << nothingToConvertFilex << nothingToConvertFilex;
+
+    const Node convertFilex1(QUrl("filex://xyz-123/hello/world"));
+    QTest::newRow("convertFilex1") << convertFilex1 << Node(QUrl("file:///media/XO-Y4/hello/world"));
+
+    const Node convertFilex2(QUrl("filex://xyz-123"));
+    QTest::newRow("convertFilex2") << convertFilex2 << Node(QUrl("file:///media/XO-Y4"));
+
+    const Node convertnfs(QUrl("nfs://thehost/solid-path"));
+    QTest::newRow("convertnfs") << convertnfs << Node(QUrl("file:///media/nfs"));
+}
+
+void RemovableMediaModelTest::testConvertFilxUrl()
+{
+    QFETCH(Node, original);
+    QFETCH(Node, converted);
+
+    QCOMPARE(m_rmModel->convertFilexUrl(original), converted);
+}
+
+void RemovableMediaModelTest::testConvertFilxUrls_data()
+{
+    QTest::addColumn<Statement>( "original" );
+    QTest::addColumn<Statement>( "converted" );
+
+    const Statement randomStatement(QUrl("nepomuk:/res/xyz"), QUrl("onto:someProp"), LiteralValue("foobar"));
+    QTest::newRow("noFileUrls") << randomStatement << randomStatement;
+
+    const Statement randomFilexSubject(QUrl("filex://123-123/tmp/test"), QUrl("onto:someProp"), LiteralValue("foobar"));
+    QTest::newRow("randomFilexUrlInSubject") << randomFilexSubject << randomFilexSubject;
+
+    const Statement convertableFilexSubject(QUrl("filex://xyz-123/test.txt"), QUrl("onto:someProp"), LiteralValue("foobar"));
+    QTest::newRow("convertableFilexUrlInSubject") << convertableFilexSubject << convertableFilexSubject;
+
+    const Statement convertableFilexObjectWithoutNieUrl(QUrl("nepomuk:/res/xyz"), QUrl("onto:someProp"), QUrl("filex://xyz-123/test.txt"));
+    QTest::newRow("convertableFilexUrlInObjectWithoutNieUrl") << convertableFilexObjectWithoutNieUrl << convertableFilexObjectWithoutNieUrl;
+
+    const Statement convertableFilexObjectWithNieUrl1_original(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("filex://xyz-123/test.txt"));
+    const Statement convertableFilexObjectWithNieUrl1_converted(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("file:///media/XO-Y4/test.txt"));
+    QTest::newRow("convertableFilexUrlInObjectWithNieUrl1") << convertableFilexObjectWithNieUrl1_original << convertableFilexObjectWithNieUrl1_converted;
+
+    const Statement convertableFilexObjectWithNieUrl2_original(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("filex://xyz-123"));
+    const Statement convertableFilexObjectWithNieUrl2_converted(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("file:///media/XO-Y4"));
+    QTest::newRow("convertableFilexUrlInObjectWithNieUrl2") << convertableFilexObjectWithNieUrl2_original << convertableFilexObjectWithNieUrl2_converted;
+
+    const Statement convertableFilexObjectWithNieUrl3_original(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("nfs://thehost/solid-path/test.txt"));
+    const Statement convertableFilexObjectWithNieUrl3_converted(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("file:///media/nfs/test.txt"));
+    QTest::newRow("convertableFileUrlInObjectWithNieUrl3") << convertableFilexObjectWithNieUrl3_original << convertableFilexObjectWithNieUrl3_converted;
+
+    const Statement convertableFilexObjectWithNieUrl4_original(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("nfs://thehost/solid-path"));
+    const Statement convertableFilexObjectWithNieUrl4_converted(QUrl("nepomuk:/res/xyz"), NIE::url(), QUrl("file:///media/nfs"));
+    QTest::newRow("convertableFileUrlInObjectWithNieUrl4") << convertableFilexObjectWithNieUrl4_original << convertableFilexObjectWithNieUrl4_converted;
+}
+
+void RemovableMediaModelTest::testConvertFilxUrls()
+{
+    QFETCH(Statement, original);
+    QFETCH(Statement, converted);
+
+    QCOMPARE(m_rmModel->convertFilexUrls(original), converted);
+}
+
+void RemovableMediaModelTest::testConversionWithUnmount()
+{
+    KUrl fileXUrl("filex://xyz-123/hello/world");
+    KUrl fileUrl("file:///media/XO-Y4/hello/world");
+
+    // device mounted
+    plugDevice(QLatin1String(s_udiXyz123));
+
+    // conversion should work
+    QCOMPARE(m_rmModel->convertFileUrl(fileUrl), Soprano::Node(fileXUrl));
+    QCOMPARE(m_rmModel->convertFilexUrl(fileXUrl), Soprano::Node(fileUrl));
+
+
+    // add some data to query
+    m_rmModel->addStatement(QUrl("nepomuk:/res/foobar"), NIE::url(), fileUrl);
+
+    // make sure it is converted when queried
+    QCOMPARE(m_rmModel->listStatements(QUrl("nepomuk:/res/foobar"), NIE::url(), Soprano::Node()).allElements().count(), 1);
+    QCOMPARE(m_rmModel->listStatements(QUrl("nepomuk:/res/foobar"), NIE::url(), Soprano::Node()).allElements().first().object(),
+             Soprano::Node(fileUrl));
+    QCOMPARE(m_rmModel->executeQuery(QString::fromLatin1("select ?u where { <nepomuk:/res/foobar> ?p ?u . }"),
+                                     Soprano::Query::QueryLanguageSparql).iterateBindings(0).allElements().count(), 1);
+    QCOMPARE(m_rmModel->executeQuery(QString::fromLatin1("select ?u where { <nepomuk:/res/foobar> ?p ?u . }"),
+                                     Soprano::Query::QueryLanguageSparql).iterateBindings(0).allElements().first(),
+             Soprano::Node(fileUrl));
+
+
+    // unmount device
+    unplugDevice(s_udiXyz123);
+
+    // now conversion should do noting
+    QCOMPARE(m_rmModel->convertFileUrl(fileUrl), Soprano::Node(fileUrl));
+    QCOMPARE(m_rmModel->convertFilexUrl(fileXUrl), Soprano::Node(fileXUrl));
+
+    // make sure nothing is converted anymore
+    QCOMPARE(m_rmModel->listStatements(QUrl("nepomuk:/res/foobar"), NIE::url(), Soprano::Node()).allElements().count(), 1);
+    QCOMPARE(m_rmModel->listStatements(QUrl("nepomuk:/res/foobar"), NIE::url(), Soprano::Node()).allElements().first().object(),
+             Soprano::Node(fileXUrl));
+    QCOMPARE(m_rmModel->executeQuery(QString::fromLatin1("select ?u where { <nepomuk:/res/foobar> ?p ?u . }"),
+                                     Soprano::Query::QueryLanguageSparql).iterateBindings(0).allElements().count(), 1);
+    QCOMPARE(m_rmModel->executeQuery(QString::fromLatin1("select ?u where { <nepomuk:/res/foobar> ?p ?u . }"),
+                                     Soprano::Query::QueryLanguageSparql).iterateBindings(0).allElements().first(),
+             Soprano::Node(fileXUrl));
+
+
+    // re-plug device for other tests
+    plugDevice(QLatin1String(s_udiXyz123));
+}
+
+QTEST_KDEMAIN_CORE(RemovableMediaModelTest)
+
+#include "removablemediamodeltest.moc"
diff --git a/nepomuk/services/storage/test/removablemediamodeltest.h b/nepomuk/services/storage/test/removablemediamodeltest.h
new file mode 100644
index 0000000..e7723ce
--- /dev/null
+++ b/nepomuk/services/storage/test/removablemediamodeltest.h
@@ -0,0 +1,56 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef REMOVABLEMEDIAMODELTEST_H
+#define REMOVABLEMEDIAMODELTEST_H
+
+#include <QObject>
+#include <QStringList>
+
+namespace Nepomuk {
+class RemovableMediaModel;
+}
+namespace Soprano {
+class Model;
+}
+
+class RemovableMediaModelTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void testConvertFileUrlsInStatement_data();
+    void testConvertFileUrlsInStatement();
+    void testConvertFileUrlsInQuery_data();
+    void testConvertFileUrlsInQuery();
+    void testConvertFilxUrl_data();
+    void testConvertFilxUrl();
+    void testConvertFilxUrls_data();
+    void testConvertFilxUrls();
+    void testConversionWithUnmount();
+
+private:
+    Soprano::Model* m_model;
+    Nepomuk::RemovableMediaModel* m_rmModel;
+};
+
+#endif
diff --git a/nepomuk/services/storage/test/resourcewatchertest.cpp b/nepomuk/services/storage/test/resourcewatchertest.cpp
new file mode 100644
index 0000000..dd3034a
--- /dev/null
+++ b/nepomuk/services/storage/test/resourcewatchertest.cpp
@@ -0,0 +1,216 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "resourcewatchertest.h"
+#include "../datamanagementmodel.h"
+#include "../classandpropertytree.h"
+#include "../resourcewatcherconnection.h"
+#include "../resourcewatchermanager.h"
+
+#include "simpleresource.h"
+#include "simpleresourcegraph.h"
+
+#include <QtTest>
+#include "qtest_kde.h"
+#include "qtest_dms.h"
+#include <QStringList>
+#include <Soprano/Soprano>
+#include <Soprano/Graph>
+#define USING_SOPRANO_NRLMODEL_UNSTABLE_API
+#include <Soprano/NRLModel>
+
+#include <KTemporaryFile>
+#include <KTempDir>
+#include <KTempDir>
+#include <KDebug>
+
+#include <Nepomuk/Vocabulary/NFO>
+#include <Nepomuk/Vocabulary/NMM>
+#include <Nepomuk/Vocabulary/NCO>
+#include <Nepomuk/Vocabulary/NIE>
+#include <Nepomuk/ResourceManager>
+
+using namespace Soprano;
+using namespace Soprano::Vocabulary;
+using namespace Nepomuk;
+using namespace Nepomuk::Vocabulary;
+
+
+void ResourceWatcherTest::resetModel()
+{
+    // remove all the junk from previous tests
+    m_model->removeAllStatements();
+
+    // add some classes and properties
+    QUrl graph("graph:/onto");
+    Nepomuk::insertOntologies( m_model, graph );
+
+    // rebuild the internals of the data management model
+    m_classAndPropertyTree->rebuildTree(m_dmModel);
+}
+
+
+void ResourceWatcherTest::initTestCase()
+{
+    const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByName( "virtuosobackend" );
+    QVERIFY( backend );
+    m_storageDir = new KTempDir();
+    m_model = backend->createModel( Soprano::BackendSettings() << Soprano::BackendSetting(Soprano::BackendOptionStorageDir, m_storageDir->name()) );
+    QVERIFY( m_model );
+
+    // DataManagementModel relies on the ussage of a NRLModel in the storage service
+    m_nrlModel = new Soprano::NRLModel(m_model);
+    m_classAndPropertyTree = new Nepomuk::ClassAndPropertyTree(this);
+    m_dmModel = new Nepomuk::DataManagementModel(m_classAndPropertyTree, m_nrlModel);
+}
+
+void ResourceWatcherTest::cleanupTestCase()
+{
+    delete m_dmModel;
+    delete m_nrlModel;
+    delete m_model;
+    delete m_storageDir;
+    delete m_classAndPropertyTree;
+}
+
+void ResourceWatcherTest::init()
+{
+    resetModel();
+}
+
+void ResourceWatcherTest::testPropertyAddedSignal()
+{
+    // create a dummy resource which we will use
+    const QUrl resA = m_dmModel->createResource(QList<QUrl>() << QUrl("class:/typeA"), QString(), QString(), QLatin1String("A"));
+
+    // no error should be generated after the above method is executed.
+    QVERIFY(!m_dmModel->lastError());
+
+    // create a connection which listens to changes in res:/A
+    Nepomuk::ResourceWatcherConnection* con = m_dmModel->resourceWatcherManager()->createConnection(QList<QUrl>() << resA, QList<QUrl>(), QList<QUrl>());
+    QVERIFY(!m_dmModel->lastError());
+
+    // spy for the propertyAdded signal
+    QSignalSpy spy(con, SIGNAL(propertyAdded(QString, QString, QVariant)));
+
+    // change the resource
+    m_dmModel->setProperty(QList<QUrl>() << resA, NAO::prefLabel(), QVariantList() << QLatin1String("foobar"), QLatin1String("A"));
+    QVERIFY(!m_dmModel->lastError());
+
+    // check that we actually got one signal
+    QCOMPARE( spy.count(), 1 );
+
+    // check that we got the correct values
+    QList<QVariant> args = spy.takeFirst();
+
+    // 1 param: the resource
+    QCOMPARE(args[0].toString(), resA.toString());
+
+    // 2 param: the property
+    QCOMPARE(args[1].toString(), NAO::prefLabel().toString());
+
+    // 3 param: the value
+    QCOMPARE(args[2].value<QVariant>(), QVariant(QString(QLatin1String("foobar"))));
+
+    // cleanup
+    con->deleteLater();
+}
+
+void ResourceWatcherTest::testPropertyRemovedSignal()
+{
+    const QUrl resA = m_dmModel->createResource(QList<QUrl>() << QUrl("class:/typeA"), QLatin1String("foobar"), QString(), QLatin1String("A"));
+    QVERIFY(!m_dmModel->lastError());
+
+    Nepomuk::ResourceWatcherConnection* con = m_dmModel->resourceWatcherManager()->createConnection(QList<QUrl>() << resA, QList<QUrl>(), QList<QUrl>());
+
+    QSignalSpy spy(con, SIGNAL(propertyRemoved(QString, QString, QVariant)));
+
+    m_dmModel->removeProperty(QList<QUrl>() << resA, NAO::prefLabel(), QVariantList() << QLatin1String("foobar"), QLatin1String("A"));
+    QVERIFY(!m_dmModel->lastError());
+
+    QCOMPARE( spy.count(), 1 );
+
+    QList<QVariant> args = spy.takeFirst();
+
+    QCOMPARE(args[0].toString(), resA.toString());
+    QCOMPARE(args[1].toString(), NAO::prefLabel().toString());
+    QCOMPARE(args[2].value<QVariant>(), QVariant(QString(QLatin1String("foobar"))));
+
+    con->deleteLater();
+}
+
+void ResourceWatcherTest::testResourceRemovedSignal()
+{
+    const QUrl resA = m_dmModel->createResource(QList<QUrl>() << QUrl("class:/typeA"), QString(), QString(), QLatin1String("A"));
+    QVERIFY(!m_dmModel->lastError());
+
+    Nepomuk::ResourceWatcherConnection* con = m_dmModel->resourceWatcherManager()->createConnection(QList<QUrl>() << resA, QList<QUrl>(), QList<QUrl>());
+    QVERIFY(!m_dmModel->lastError());
+
+    QSignalSpy spy(con, SIGNAL(resourceRemoved(QString, QStringList)));
+
+    m_dmModel->removeResources(QList<QUrl>() << resA, Nepomuk::RemovalFlags() , QLatin1String("A"));
+    QVERIFY(!m_dmModel->lastError());
+
+    QCOMPARE( spy.count(), 1 );
+
+    QList<QVariant> args = spy.takeFirst();
+
+    QCOMPARE(args[0].toString(), resA.toString());
+
+    con->deleteLater();
+}
+
+void ResourceWatcherTest::testStoreResources_createResources()
+{
+    ResourceWatcherManager *rvm = m_dmModel->resourceWatcherManager();
+    ResourceWatcherConnection* con = rvm->createConnection( QList<QUrl>(), QList<QUrl>(),
+                                                            QList<QUrl>() << NCO::Contact() );
+    QVERIFY(!m_dmModel->lastError());
+
+    SimpleResource res;
+    res.addType( NCO::Contact() );
+    res.addProperty( NCO::fullname(), QLatin1String("Haruki Murakami") );
+
+    QSignalSpy spy(con, SIGNAL(resourceCreated(QString, QStringList)));
+
+    m_dmModel->storeResources( SimpleResourceGraph() << res, QLatin1String("testApp") );
+    QVERIFY(!m_dmModel->lastError());
+
+    QCOMPARE( spy.count(), 1 );
+
+    QList<QVariant> args = spy.takeFirst();
+
+    QList< Statement > stList = m_model->listStatements( Node(), RDF::type(), NCO::Contact() ).allStatements();
+    QCOMPARE( stList.size(), 1 );
+
+    const QUrl resUri = stList.first().subject().uri();
+    QCOMPARE(args[0].toString(), resUri.toString());
+    QCOMPARE(args[1].toStringList(), QStringList() << NCO::Contact().toString() );
+
+    con->deleteLater();
+}
+
+
+QTEST_KDEMAIN_CORE(ResourceWatcherTest)
+
+#include "resourcewatchertest.moc"
diff --git a/nepomuk/services/storage/test/resourcewatchertest.h b/nepomuk/services/storage/test/resourcewatchertest.h
new file mode 100644
index 0000000..b9ef725
--- /dev/null
+++ b/nepomuk/services/storage/test/resourcewatchertest.h
@@ -0,0 +1,65 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef RESOURCEWATCHERSIGNALTEST_H
+#define RESOURCEWATCHERSIGNALTEST_H
+
+#include <QObject>
+
+namespace Soprano {
+class Model;
+class NRLModel;
+}
+namespace Nepomuk {
+class DataManagementModel;
+class ClassAndPropertyTree;
+}
+class KTempDir;
+
+class ResourceWatcherTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+
+    void testPropertyAddedSignal();
+    void testPropertyRemovedSignal();
+    void testResourceRemovedSignal();
+
+    void testStoreResources_createResources();
+private:
+    KTempDir* createNieUrlTestData();
+
+    void resetModel();
+    bool haveTrailingGraphs() const;
+
+    KTempDir* m_storageDir;
+    Soprano::Model* m_model;
+    Soprano::NRLModel* m_nrlModel;
+    Nepomuk::ClassAndPropertyTree* m_classAndPropertyTree;
+    Nepomuk::DataManagementModel* m_dmModel;
+};
+
+#endif
diff --git a/nepomuk/services/storage/test/solid/fakecomputer.xml b/nepomuk/services/storage/test/solid/fakecomputer.xml
new file mode 100644
index 0000000..e3fcfb8
--- /dev/null
+++ b/nepomuk/services/storage/test/solid/fakecomputer.xml
@@ -0,0 +1,550 @@
+<!-- Please note that in this file we indent more than necessary so that the
+     device tree is visible -->
+
+<machine>
+    <!-- This is a computer -->
+    <device udi="/org/kde/solid/fakehw/computer">
+        <property key="name">Computer</property>
+        <property key="vendor">Solid</property>
+    </device>
+
+
+        <!-- A system with its own AC adapter and a battery (like a laptop) -->
+        <device udi="/org/kde/solid/fakehw/acpi_AC">
+            <property key="name">AC Adapter</property>
+            <property key="interfaces">AcAdapter</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+            <property key="isPlugged">false</property>
+        </device>
+        <device udi="/org/kde/solid/fakehw/acpi_BAT0">
+            <property key="name">Battery Bay</property>
+            <property key="vendor">Acme Corporation</property>
+            <property key="interfaces">Battery</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+            <property key="isPlugged">true</property>
+            <property key="batteryType">primary</property>
+            <property key="chargeLevelUnit">mWh</property>
+            <property key="maxLevel">43200000</property>
+            <property key="lastFullLevel">42165000</property>
+            <property key="currentLevel">42100000</property>
+            <property key="warningLevel">140550000</property>
+            <property key="lowLevel">7027500</property>
+            <property key="voltageUnit">mV</property>
+            <property key="voltage">11999</property>
+            <property key="isRechargeable">true</property>
+            <property key="chargeState">discharging</property>
+        </device>
+
+
+
+        <!-- So that it looks like a laptop,
+             provide this computer a few buttons:
+               - power button
+               - sleep button
+               - lid switch -->
+        <device udi="/org/kde/solid/fakehw/acpi_PWB">
+            <property key="name">Power Button</property>
+            <property key="interfaces">Button</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+
+            <property key="type">PowerButton</property>
+            <property key="hasState">false</property>
+        </device>
+        <device udi="/org/kde/solid/fakehw/acpi_SLPB">
+            <property key="name">Sleep Button</property>
+            <property key="interfaces">Button</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+
+            <property key="type">SleepButton</property>
+            <property key="hasState">false</property>
+        </device>
+        <device udi="/org/kde/solid/fakehw/acpi_LID0">
+            <property key="name">Lid Switch</property>
+            <property key="interfaces">Button</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+
+            <property key="type">LidButton</property>
+            <property key="hasState">true</property>
+            <property key="stateValue">false</property>
+        </device>
+
+
+
+        <!-- Two CPUs -->
+        <device udi="/org/kde/solid/fakehw/acpi_CPU0">
+            <property key="name">Solid Processor #0</property>
+            <property key="interfaces">Processor</property>
+            <property key="vendor">Acme Corporation</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+            <property key="number">0</property>
+            <property key="maxSpeed">3200</property>
+            <property key="canChangeFrequency">true</property>
+            <property key="instructionSets">mmx,sse</property>
+        </device>
+        <device udi="/org/kde/solid/fakehw/acpi_CPU1">
+            <property key="name">Solid Processor #1</property>
+            <property key="interfaces">Processor</property>
+            <property key="vendor">Acme Corporation</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+            <property key="number">1</property>
+            <property key="maxSpeed">3200</property>
+            <property key="canChangeFrequency">true</property>
+        </device>
+
+
+
+        <!-- Platform Device for a floppy drive -->
+        <device udi="/org/kde/solid/fakehw/platform_floppy_0">
+            <property key="name">Platform Device (floppy)</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+        </device>
+            <!-- The actual floppy device -->
+            <device udi="/org/kde/solid/fakehw/platform_floppy_0_storage">
+                <property key="name">PC Floppy Drive</property>
+                <property key="interfaces">StorageDrive,Block</property>
+                <property key="parent">/org/kde/solid/fakehw/platform_floppy_0</property>
+
+                <property key="minor">0</property>
+                <property key="major">2</property>
+                <property key="device">/dev/fd0</property>
+
+                <property key="bus">platform</property>
+                <property key="driveType">floppy</property>
+                <property key="isRemovable">true</property>
+                <property key="isEjectRequired">false</property>
+                <property key="isHotpluggable">false</property>
+                <property key="isMediaCheckEnabled">false</property>
+            </device>
+                <!-- A (generally) virtual volume tracking the floppy drive state -->
+                <device udi="/org/kde/solid/fakehw/platform_floppy_0_storage_virt_volume">
+                    <property key="name">Floppy Disk</property>
+                    <property key="interfaces">Block,StorageVolume,StorageAccess</property>
+                    <property key="parent">/org/kde/solid/fakehw/platform_floppy_0_storage</property>
+
+                    <property key="minor">0</property>
+                    <property key="major">2</property>
+                    <property key="device">/dev/fd0</property>
+
+                    <property key="isIgnored">false</property>
+                    <property key="isMounted">true</property>
+                    <property key="mountPoint">/media/floppy0</property>
+                    <property key="usage">filesystem</property>
+                </device>
+
+
+
+        <!-- Primary IDE controller -->
+        <device udi="/org/kde/solid/fakehw/pci_001">
+            <property key="name">99021 IDE Controller #1</property>
+            <property key="vendor">Acme Corporation</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+        </device>
+
+            <!-- Master device... -->
+            <device udi="/org/kde/solid/fakehw/pci_001_ide_0_0">
+                <property key="name">IDE device (master)</property>
+                <property key="parent">/org/kde/solid/fakehw/pci_001</property>
+            </device>
+                <!-- ... is a 250GB disk... -->
+                <device udi="/org/kde/solid/fakehw/storage_serial_HD56890I">
+                    <property key="name">HD250GB</property>
+                    <property key="vendor">Acme Corporation</property>
+                    <property key="interfaces">StorageDrive,Block</property>
+                    <property key="parent">/org/kde/solid/fakehw/pci_001_ide_0_0</property>
+
+                    <property key="minor">0</property>
+                    <property key="major">3</property>
+                    <property key="device">/dev/hda</property>
+
+                    <property key="bus">scsi</property>
+                    <property key="driveType">disk</property>
+                    <property key="isRemovable">false</property>
+                    <property key="isEjectRequired">false</property>
+                    <property key="isHotpluggable">false</property>
+                    <property key="isMediaCheckEnabled">false</property>
+                    <property key="product">HD250GBSATA</property>
+                </device>
+                    <!-- ... with five partitions:
+                              - one physical partition (the root /, ext3, 20GB)
+                              - one extended containing three logical volumes:
+                                  - a swap volume (2GB)
+                                  - /home volume (xfs, 208GB)
+                                  - /foreign volume (ntfs, 20GB)
+                    -->
+                    <device udi="/org/kde/solid/fakehw/volume_uuid_feedface">
+                        <property key="name">/</property>
+                        <property key="interfaces">Block,StorageVolume,StorageAccess</property>
+                        <property key="parent">/org/kde/solid/fakehw/storage_serial_HD56890I</property>
+
+                        <property key="minor">1</property>
+                        <property key="major">3</property>
+                        <property key="device">/dev/hda1</property>
+
+                        <property key="isIgnored">true</property>
+                        <property key="isMounted">true</property>
+                        <property key="mountPoint">/</property>
+                        <property key="usage">filesystem</property>
+                        <property key="fsType">ext3</property>
+                        <property key="label">Root</property>
+                        <property key="uuid">feedface</property>
+                        <property key="size">21474836480</property>
+                    </device>
+                    <device udi="/org/kde/solid/fakehw/volume_uuid_c0ffee">
+                        <property key="name">/home</property>
+                        <property key="interfaces">Block,StorageVolume,StorageAccess</property>
+                        <property key="parent">/org/kde/solid/fakehw/storage_serial_HD56890I</property>
+
+                        <property key="minor">6</property>
+                        <property key="major">3</property>
+                        <property key="device">/dev/hda6</property>
+
+                        <property key="isIgnored">true</property>
+                        <property key="isMounted">true</property>
+                        <property key="mountPoint">/home</property>
+                        <property key="usage">filesystem</property>
+                        <property key="fsType">xfs</property>
+                        <property key="label">Home</property>
+                        <property key="uuid">c0ffee</property>
+                        <property key="size">223338299392</property>
+                    </device>
+                    <device udi="/org/kde/solid/fakehw/volume_uuid_f00ba7">
+                        <property key="name">/foreign</property>
+                        <property key="interfaces">Block,StorageVolume,StorageAccess</property>
+                        <property key="parent">/org/kde/solid/fakehw/storage_serial_HD56890I</property>
+
+                        <property key="minor">7</property>
+                        <property key="major">3</property>
+                        <property key="device">/dev/hda7</property>
+
+                        <property key="isIgnored">false</property>
+                        <property key="isMounted">true</property>
+                        <property key="mountPoint">/foreign</property>
+                        <property key="usage">filesystem</property>
+                        <property key="fsType">ntfs</property>
+                        <property key="label">Foreign</property>
+                        <property key="uuid">f00ba7</property>
+                        <property key="size">21474836480</property>
+                    </device>
+                    <device udi="/org/kde/solid/fakehw/volume_part2_size_1024">
+                        <property key="name">StorageVolume</property>
+                        <property key="interfaces">Block,StorageVolume,StorageAccess</property>
+                        <property key="parent">/org/kde/solid/fakehw/storage_serial_HD56890I</property>
+
+                        <property key="minor">2</property>
+                        <property key="major">3</property>
+                        <property key="device">/dev/hda2</property>
+
+                        <property key="isIgnored">true</property>
+                        <property key="isMounted">false</property>
+                        <property key="usage">other</property>
+                        <property key="size">1024</property>
+                    </device>
+                    <device udi="/org/kde/solid/fakehw/volume_part5_size_1048576">
+                        <property key="name">StorageVolume (swap)</property>
+                        <property key="interfaces">Block,StorageVolume,StorageAccess</property>
+                        <property key="parent">/org/kde/solid/fakehw/storage_serial_HD56890I</property>
+
+                        <property key="minor">5</property>
+                        <property key="major">3</property>
+                        <property key="device">/dev/hda5</property>
+
+                        <property key="isIgnored">true</property>
+                        <property key="isMounted">false</property>
+                        <property key="usage">other</property>
+                        <property key="fsType">swap</property>
+                        <property key="size">2147483648</property>
+                    </device>
+
+
+        <!-- Secondary IDE controller -->
+        <device udi="/org/kde/solid/fakehw/pci_002">
+            <property key="name">99021 IDE Controller #2</property>
+            <property key="vendor">Acme Corporation</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+        </device>
+
+            <!-- Master device... -->
+            <device udi="/org/kde/solid/fakehw/pci_002_ide_1_0">
+                <property key="name">IDE device (master)</property>
+                <property key="parent">/org/kde/solid/fakehw/pci_002</property>
+            </device>
+                <!-- ... is a DVD writer... -->
+                <device udi="/org/kde/solid/fakehw/storage_model_solid_writer">
+                    <property key="name">Solid IDE DVD Writer</property>
+                    <property key="vendor">Acme Corporation</property>
+                    <property key="interfaces">Block,StorageDrive,OpticalDrive</property>
+                    <property key="parent">/org/kde/solid/fakehw/pci_002_ide_1_0</property>
+
+                    <property key="minor">0</property>
+                    <property key="major">22</property>
+                    <property key="device">/dev/hdc</property>
+
+                    <property key="bus">ide</property>
+                    <property key="driveType">cdrom</property>
+                    <property key="isRemovable">true</property>
+                    <property key="isEjectRequired">true</property>
+                    <property key="isHotpluggable">false</property>
+                    <property key="isMediaCheckEnabled">true</property>
+                    <property key="product">Solid DVD Writer</property>
+
+                    <property key="supportedMedia">cdr,cdrw,dvd,dvdr,dvdrw</property>
+                    <property key="readSpeed">4234</property>
+                    <property key="writeSpeed">4234</property>
+                    <property key="writeSpeeds">4234,2822,2117,1411,706</property>
+                </device>
+                    <!-- ... with a cd-r in it -->
+                    <device udi="/org/kde/solid/fakehw/volume_uuid_5011">
+                        <property key="name">FooDistro i386</property>
+                        <property key="interfaces">Block,StorageVolume,OpticalDisc,StorageAccess</property>
+                        <property key="parent">/org/kde/solid/fakehw/storage_model_solid_writer</property>
+
+                        <property key="discType">cd_rw</property>
+                        <property key="isAppendable">false</property>
+                        <property key="isRewritable">true</property>
+                        <property key="isBlank">false</property>
+                        <property key="availableContent">data</property>
+                        <property key="mountPoint">/media/cdrom</property>
+
+                        <property key="uuid">5011</property>
+                        <property key="size">731047936</property>
+                        <property key="label">FooDistro i386</property>
+                    </device>
+
+            <!-- Slave device... -->
+            <device udi="/org/kde/solid/fakehw/pci_002_ide_1_1">
+                <property key="name">IDE device (slave)</property>
+                <property key="parent">/org/kde/solid/fakehw/pci_002</property>
+            </device>
+                <!-- ... is a DVD reader... -->
+                <device udi="/org/kde/solid/fakehw/storage_model_solid_reader">
+                    <property key="name">Solid IDE DVD Reader</property>
+                    <property key="vendor">Acme Corporation</property>
+                    <property key="interfaces">Block,StorageDrive,OpticalDrive</property>
+                    <property key="parent">/org/kde/solid/fakehw/pci_002_ide_1_1</property>
+
+                    <property key="minor">0</property>
+                    <property key="major">22</property>
+                    <property key="device">/dev/hdc</property>
+
+                    <property key="bus">ide</property>
+                    <property key="driveType">cdrom</property>
+                    <property key="isRemovable">true</property>
+                    <property key="isEjectRequired">true</property>
+                    <property key="isHotpluggable">false</property>
+                    <property key="isMediaCheckEnabled">true</property>
+                    <property key="product">Solid DVD Reader</property>
+
+                    <property key="supportedMedia">cdr,cdrw,dvd,dvdr,dvdrw,dvdram,dvdplusr,dvdplusrw</property>
+                    <property key="readSpeed">4234</property>
+                </device>
+                    <!-- ... with a DVD Video in it -->
+                    <device udi="/org/kde/solid/fakehw/volume_label_SOLIDMAN_BEGINS">
+                        <property key="name">SolidMan Begins</property>
+                        <property key="interfaces">Block,StorageVolume,OpticalDisc,StorageAccess</property>
+                        <property key="parent">/org/kde/solid/fakehw/storage_model_solid_reader</property>
+
+                        <property key="discType">dvd_rom</property>
+                        <property key="isAppendable">false</property>
+                        <property key="isRewritable">false</property>
+                        <property key="isBlank">false</property>
+                        <property key="availableContent">dvdvideo</property>
+
+                        <property key="uuid">5012</property>
+                        <property key="size">8033075200</property>
+                        <property key="label">SolidMan Begins</property>
+                        <property key="isMounted">true</property>
+                        <property key="mountPoint">/media/dvd</property>
+                        <property key="usage">filesystem</property>
+                        <property key="fsType">udf</property>
+                        <property key="label">SOLIDMAN_BEGINS</property>
+                        <property key="size">4235423524</property>
+                    </device>
+
+
+
+        <!-- First USB Controller -->
+        <device udi="/org/kde/solid/fakehw/pci_8086_265c">
+            <property key="name">99021 USB2 EHCI Controller #1</property>
+            <property key="vendor">Acme Corporation</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+        </device>
+            <!-- Host Controller -->
+            <device udi="/org/kde/solid/fakehw/usb_device_0_0_1d_7">
+                <property key="name">EHCI Host Controller</property>
+                <property key="vendor">Kernel ehci_hcd</property>
+                <property key="parent">/org/kde/solid/fakehw/pci_8086_265c</property>
+            </device>
+                <!-- USB Device -->
+                <device udi="/org/kde/solid/fakehw/usb_device_4e8_5041">
+                    <property key="name">Acme XO-Y4</property>
+                    <property key="parent">/org/kde/solid/fakehw/usb_device_0_0_1d_7</property>
+                </device>
+                    <!-- Mass Storage Interface -->
+                    <device udi="/org/kde/solid/fakehw/usb_device_4e8_5041_if0">
+                        <property key="name">USB Mass Storage Inferface</property>
+                        <property key="parent">/org/kde/solid/fakehw/usb_device_4e8_5041</property>
+                    </device>
+                        <!-- SCSI Adapter -->
+                        <device udi="/org/kde/solid/fakehw/usb_device_4e8_5041_if0_scsi_host">
+                            <property key="name">SCSI Host Adapter</property>
+                            <property key="parent">/org/kde/solid/fakehw/usb_device_4e8_5041_if0</property>
+                        </device>
+                        <!-- SCSI Device -->
+                            <device udi="/org/kde/solid/fakehw/usb_device_4e8_5041_if0_scsi_host_scsi_device_lun0">
+                                <property key="name">SCSI Device</property>
+                                <property key="parent">/org/kde/solid/fakehw/usb_device_4e8_5041_if0_scsi_host</property>
+                            </device>
+                                <!-- We finally find the storage device, which is a portable media player... -->
+                                <device udi="/org/kde/solid/fakehw/storage_serial_XOY4_5206">
+                                    <property key="name">XO-Y4</property>
+                                    <property key="vendor">Acme Electronics</property>
+                                    <property key="interfaces">StorageDrive,Block,PortableMediaPlayer</property>
+                                    <property key="parent">/org/kde/solid/fakehw/usb_device_4e8_5041_if0_scsi_host_scsi_device_lun0</property>
+
+                                    <property key="minor">0</property>
+                                    <property key="major">8</property>
+                                    <property key="device">/dev/sda</property>
+
+                                    <property key="bus">usb</property>
+                                    <property key="driveType">disk</property>
+                                    <property key="isRemovable">true</property>
+                                    <property key="isEjectRequired">true</property>
+                                    <property key="isHotpluggable">true</property>
+                                    <property key="isMediaCheckEnabled">true</property>
+                                    <property key="product">XO-Y4</property>
+
+                                    <property key="accessMethod">MassStorage</property>
+                                    <property key="outputFormats">audio/x-mp3</property>
+                                    <property key="inputFormats">audio/x-wav,audio/x-mp3,audio/vorbis</property>
+                                    <property key="playlistFormats">audio/x-mpegurl</property>
+                                </device>
+                                    <!-- ... with a partition since it's a USB Mass Storage device -->
+                                    <device udi="/org/kde/solid/fakehw/volume_part1_size_993284096">
+                                        <property key="name">StorageVolume (vfat)</property>
+                                        <property key="interfaces">Block,StorageVolume,StorageAccess</property>
+                                        <property key="parent">/org/kde/solid/fakehw/storage_serial_XOY4_5206</property>
+					<property key="uuid">XYZ-123</property>
+
+                                        <property key="minor">1</property>
+                                        <property key="major">8</property>
+                                        <property key="device">/dev/sda1</property>
+
+                                        <property key="isIgnored">false</property>
+                                        <property key="isMounted">true</property>
+                                        <property key="mountPoint">/media/XO-Y4</property>
+                                        <property key="usage">filesystem</property>
+                                        <property key="fsType">vfat</property>
+                                        <property key="size">993284096</property>
+                                    </device>
+
+
+
+        <!-- Second USB Controller -->
+        <device udi="/org/kde/solid/fakehw/pci_8086_265d">
+            <property key="name">99021 USB UHCI #1</property>
+            <property key="vendor">Acme Corporation</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+        </device>
+            <!-- Host Controller -->
+            <device udi="/org/kde/solid/fakehw/usb_device_0_0_1d_2">
+                <property key="name">UHCI Host Controller</property>
+                <property key="vendor">Kernel uhci_hcd</property>
+                <property key="parent">/org/kde/solid/fakehw/pci_8086_265d</property>
+            </device>
+
+                <!-- USB Device #1 -->
+                <device udi="/org/kde/solid/fakehw/usb_device_4a9_30b9_noserial">
+                    <property key="name">PowerBullet A35</property>
+                    <property key="vendor">Firearm Inc.</property>
+                    <property key="parent">/org/kde/solid/fakehw/usb_device_0_0_1d_2</property>
+                </device>
+                    <!-- We finally find the camera interface -->
+                    <device udi="/org/kde/solid/fakehw/usb_device_4a9_30b9_noserial_if0">
+                        <property key="name">USB Imaging Interface</property>
+                        <property key="interfaces">Camera</property>
+                        <property key="parent">/org/kde/solid/fakehw/usb_device_4a9_30b9_noserial</property>
+                        <property key="accessMethod">ptp</property>
+                        <property key="gphotoSupport">true</property>
+                    </device>
+
+        <!-- PCI Bridge -->
+        <device udi="/org/kde/solid/fakehw/pci_8086_2448">
+            <property key="name">99021 PCI Bridge</property>
+            <property key="vendor">Acme Corporation</property>
+            <property key="parent">/org/kde/solid/fakehw/computer</property>
+        </device>
+
+            <!-- PCI device #1 -->
+            <device udi="/org/kde/solid/fakehw/pci_3452_7890">
+                <property key="name">Wireless 1144AH</property>
+                <property key="vendor">Acme Corporation</property>
+                <property key="parent">/org/kde/solid/fakehw/pci_8086_2448</property>
+            </device>
+                <!-- Wireless network interface -->
+                <device udi="/org/kde/solid/fakehw/net_00_1a_eb_c9_64_16">
+                    <property key="name">WLAN Interface</property>
+                    <property key="interfaces">NetworkInterface</property>
+                    <property key="parent">/org/kde/solid/fakehw/pci_3452_7890</property>
+                    <property key="ifaceName">wlan0</property>
+                    <property key="wireless">true</property>
+                    <property key="hwAddress">00:1A:EB:C9:64:16</property>
+                    <property key="macAddress">0x001AEBC96416</property>
+                </device>
+
+            <!-- PCI device #2 -->
+            <device udi="/org/kde/solid/fakehw/pci_8904_5e21">
+                <property key="name">RC-1893</property>
+                <property key="vendor">ReallyChip Corporation</property>
+                <property key="parent">/org/kde/solid/fakehw/pci_8086_2448</property>
+            </device>
+                <!-- Ethernet network interface -->
+                <device udi="/org/kde/solid/fakehw/net_00_1a_eb_c9_64_15">
+                    <property key="name">Networking Interface</property>
+                    <property key="interfaces">NetworkInterface</property>
+                    <property key="parent">/org/kde/solid/fakehw/pci_8904_5e21</property>
+                    <property key="ifaceName">eth0</property>
+                    <property key="wireless">false</property>
+                    <property key="hwAddress">00:1A:EB:C9:64:15</property>
+                    <property key="macAddress">0x001AEBC96415</property>
+                </device>
+
+            <!-- PCI device #3 -->
+            <device udi="/org/kde/solid/fakehw/pci_8843_6a11">
+                <property key="name">VisioTNT</property>
+                <property key="vendor">DigitalVid Ltd</property>
+                <property key="parent">/org/kde/solid/fakehw/pci_8086_2448</property>
+            </device>
+                <!-- Digital video broadcasting interface -->
+                <device udi="/org/kde/solid/fakehw/pci_8843_6a11_dvb1">
+                    <property key="name">DVB Interface</property>
+                    <property key="interfaces">DvbInterface</property>
+                    <property key="parent">/org/kde/solid/fakehw/pci_8843_6a11</property>
+                    <property key="device">/dev/broadcast0/demux</property>
+                    <property key="deviceAdapter">2</property>
+                    <property key="deviceType">demux</property>
+                    <property key="deviceIndex">1</property>
+                </device>
+
+    <device udi="/org/kde/solid/fakehw/fstab">
+        <property key="name">Network Shares</property>
+        <property key="vendor">KDE</property>
+        <property key="product">Network Shares</property>
+        <property key="parent">/org/kde/solid/fakehw/computer</property>
+    </device>
+
+        <device udi="/org/kde/solid/fakehw/fstab/thehost/solidpath">
+            <property key="parent">/org/kde/fstab</property>
+            <property key="interfaces">StorageAccess,NetworkShare</property>
+
+            <property key="vendor">/solidpath</property>
+            <property key="product">thehost</property>
+
+            <property key="type">nfs</property>
+            <property key="url">nfs://thehost/solid-path</property>
+
+            <property key="filePath">/media/nfs</property>
+            <property key="isIgnored">false</property>
+            <property key="isMounted">true</property>
+            <property key="mountPoint">/media/nfs</property>
+        </device>
+</machine>
diff --git a/nepomuk/services/storage/test/typevisibilitytreetest.cpp b/nepomuk/services/storage/test/typevisibilitytreetest.cpp
deleted file mode 100644
index 0a79c05..0000000
--- a/nepomuk/services/storage/test/typevisibilitytreetest.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "typevisibilitytreetest.h"
-#include "../typevisibilitytree.h"
-
-#include <QtTest>
-#include "qtest_kde.h"
-
-#include <Soprano/Soprano>
-
-#include <ktempdir.h>
-
-using namespace Soprano;
-
-void TypeVisibilityTreeTest::initTestCase()
-{
-    // we need to use a Virtuoso model as tmp model since redland misses important SPARQL features
-    // that are used by libnepomuk below
-    const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByName( "virtuosobackend" );
-    QVERIFY( backend );
-    m_storageDir = new KTempDir();
-    m_model = backend->createModel( Soprano::BackendSettings() << Soprano::BackendSetting(Soprano::BackendOptionStorageDir, m_storageDir->name()) );
-    QVERIFY( m_model );
-
-    m_typeTree = new TypeVisibilityTree( m_model );
-}
-
-void TypeVisibilityTreeTest::cleanupTestCase()
-{
-    delete m_model;
-    delete m_typeTree;
-    delete m_storageDir;
-}
-
-
-void TypeVisibilityTreeTest::init()
-{
-    m_model->removeAllStatements();
-
-    // we create one fake ontology
-    //
-    // situations we need to test:
-    // * class that is marked visible should stay visible
-    // * class that is marked invisible should stay invisible
-    // * non-marked subclass of visible should be visible, too
-    // * non-marked subclass of invisible should be invisible, too
-    // * marked subclass should keep its own visiblity and not inherit from parent
-    // * whole branch should inherit from parent
-    // * if one parent is visible the class is visible, too, even if N other parents are not
-    // * if all parents are invisible, the class is invisible, even if higher up in the branch a class is visible
-    // * properly handle loops (as in: do not run into an endless loop)
-    //
-    // A
-    // |- B - invisible
-    //    |- C
-    //       |- D - visible
-    //          |- E
-    //             |- F
-    //    |- G
-    //
-    // AA - invisible
-    // | - F
-    // | - G
-    //
-    // X
-    // |- Y - invisible
-    //    |- Z
-    //       |- X
-
-    QUrl graph("graph:/onto");
-    m_model->addStatement( graph, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::Ontology(), graph );
-
-    m_model->addStatement( QUrl("onto:/A"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/B"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/C"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/D"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/E"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/F"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/G"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/AA"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/X"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/Y"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-    m_model->addStatement( QUrl("onto:/Z"), Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::RDFS::Class(), graph );
-
-    m_model->addStatement( QUrl("onto:/B"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/A"), graph );
-    m_model->addStatement( QUrl("onto:/C"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/B"), graph );
-    m_model->addStatement( QUrl("onto:/D"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/C"), graph );
-    m_model->addStatement( QUrl("onto:/E"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/D"), graph );
-    m_model->addStatement( QUrl("onto:/F"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/E"), graph );
-    m_model->addStatement( QUrl("onto:/G"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/B"), graph );
-    m_model->addStatement( QUrl("onto:/F"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/AA"), graph );
-    m_model->addStatement( QUrl("onto:/G"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/AA"), graph );
-    m_model->addStatement( QUrl("onto:/Y"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/X"), graph );
-    m_model->addStatement( QUrl("onto:/Z"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/Y"), graph );
-    m_model->addStatement( QUrl("onto:/X"), Soprano::Vocabulary::RDFS::subClassOf(), QUrl("onto:/Z"), graph );
-
-    m_model->addStatement( QUrl("onto:/B"), Soprano::Vocabulary::NAO::userVisible(), LiteralValue(false), graph );
-    m_model->addStatement( QUrl("onto:/D"), Soprano::Vocabulary::NAO::userVisible(), LiteralValue(true), graph );
-    m_model->addStatement( QUrl("onto:/AA"), Soprano::Vocabulary::NAO::userVisible(), LiteralValue(false), graph );
-    m_model->addStatement( QUrl("onto:/Y"), Soprano::Vocabulary::NAO::userVisible(), LiteralValue(false), graph );
-
-    m_typeTree->rebuildTree();
-}
-
-void TypeVisibilityTreeTest::testTypeVisibilityTree()
-{
-    QVERIFY(m_typeTree->isVisible(QUrl("onto:/A")));
-    QVERIFY(!m_typeTree->isVisible(QUrl("onto:/B")));
-    QVERIFY(!m_typeTree->isVisible(QUrl("onto:/C")));
-    QVERIFY(m_typeTree->isVisible(QUrl("onto:/D")));
-    QVERIFY(m_typeTree->isVisible(QUrl("onto:/E")));
-    QVERIFY(m_typeTree->isVisible(QUrl("onto:/F")));
-    QVERIFY(!m_typeTree->isVisible(QUrl("onto:/G")));
-    QVERIFY(!m_typeTree->isVisible(QUrl("onto:/AA")));
-    QVERIFY(!m_typeTree->isVisible(QUrl("onto:/X"))); // because only top-level classes inherit from rdfs:Resource
-    QVERIFY(!m_typeTree->isVisible(QUrl("onto:/Y")));
-    QVERIFY(!m_typeTree->isVisible(QUrl("onto:/Z")));
-}
-
-QTEST_KDEMAIN_CORE(TypeVisibilityTreeTest)
-
-#include "typevisibilitytreetest.moc"
diff --git a/nepomuk/services/storage/test/typevisibilitytreetest.h b/nepomuk/services/storage/test/typevisibilitytreetest.h
deleted file mode 100644
index e2b23f6..0000000
--- a/nepomuk/services/storage/test/typevisibilitytreetest.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef TYPEVISIBILITYTREETEST_H
-#define TYPEVISIBILITYTREETEST_H
-
-#include <QObject>
-
-class KTempDir;
-class TypeVisibilityTree;
-namespace Soprano {
-class Model;
-}
-
-class TypeVisibilityTreeTest : public QObject
-{
-    Q_OBJECT
-
-private Q_SLOTS:
-    void initTestCase();
-    void cleanupTestCase();
-    void init();
-    void testTypeVisibilityTree();
-
-private:
-    KTempDir* m_storageDir;
-    Soprano::Model* m_model;
-    TypeVisibilityTree* m_typeTree;
-};
-
-#endif // TYPEVISIBILITYTREETEST_H
diff --git a/nepomuk/services/storage/typevisibilitytree.cpp b/nepomuk/services/storage/typevisibilitytree.cpp
deleted file mode 100644
index 5c6fefe..0000000
--- a/nepomuk/services/storage/typevisibilitytree.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "typevisibilitytree.h"
-
-#include <Soprano/Model>
-#include <Soprano/QueryResultIterator>
-#include <Soprano/Node>
-#include <Soprano/LiteralValue>
-#include <Soprano/Vocabulary/NAO>
-#include <Soprano/Vocabulary/RDFS>
-
-#include <QtCore/QMutexLocker>
-
-#include <KDebug>
-
-
-class TypeVisibilityTree::TypeVisibilityNode
-{
-public:
-    TypeVisibilityNode( const QUrl& r )
-        : uri( r ),
-          userVisible( 0 ) {
-    }
-
-    QUrl uri;
-    int userVisible;
-    QSet<TypeVisibilityNode*> parents;
-
-    /**
-     * Set the value of nao:userVisible.
-     * A class is visible if it has at least one visible direct parent class.
-     */
-    int updateUserVisibility( QSet<TypeVisibilityNode*>& visitedNodes ) {
-        if ( userVisible != 0 ) {
-            return userVisible;
-        }
-        else {
-            if ( !parents.isEmpty() ) {
-                for ( QSet<TypeVisibilityNode*>::iterator it = parents.begin();
-                     it != parents.end(); ++it ) {
-                    // avoid endless loops
-                    if( visitedNodes.contains(*it) )
-                        continue;
-                    visitedNodes.insert( *it );
-                    if ( (*it)->updateUserVisibility( visitedNodes ) == 1 ) {
-                        userVisible = 1;
-                        break;
-                    }
-                }
-            }
-            if ( userVisible == 0 ) {
-                // default to invisible
-                userVisible = -1;
-            }
-            kDebug() << "Setting nao:userVisible of" << uri.toString() << ( userVisible == 1 );
-            return userVisible;
-        }
-    }
-};
-
-TypeVisibilityTree::TypeVisibilityTree( Soprano::Model* model )
-    : m_model(model)
-{
-}
-
-TypeVisibilityTree::~TypeVisibilityTree()
-{
-    QMutexLocker lock( &m_mutex );
-}
-
-void TypeVisibilityTree::rebuildTree()
-{
-    QMutexLocker lock( &m_mutex );
-
-    // cleanup
-    m_visibilityHash.clear();
-
-    // we build a temporary helper tree
-    QHash<QUrl, TypeVisibilityNode*> tree;
-
-    const QString query
-        = QString::fromLatin1( "select distinct ?r ?p ?v where { "
-                               "?r a rdfs:Class . "
-                               "OPTIONAL { ?r rdfs:subClassOf ?p . ?p a rdfs:Class . } . "
-                               "OPTIONAL { ?r %1 ?v . } . "
-                               "FILTER(?r!=rdfs:Resource) . "
-                               "}" )
-            .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::NAO::userVisible() ) );
-    kDebug() << query;
-    Soprano::QueryResultIterator it
-            = m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
-    while( it.next() ) {
-        const QUrl r = it["r"].uri();
-        const Soprano::Node p = it["p"];
-        const Soprano::Node v = it["v"];
-
-        if ( !tree.contains( r ) ) {
-            TypeVisibilityNode* r_uvn = new TypeVisibilityNode( r );
-            tree.insert( r, r_uvn );
-        }
-        if( v.isLiteral() )
-            tree[r]->userVisible = (v.literal().toBool() ? 1 : -1);
-        if ( p.isResource() &&
-                p.uri() != r &&
-                p.uri() != Soprano::Vocabulary::RDFS::Resource() ) {
-            if ( !tree.contains( p.uri() ) ) {
-                TypeVisibilityNode* p_uvn = new TypeVisibilityNode( p.uri() );
-                tree.insert( p.uri(), p_uvn );
-                tree[r]->parents.insert( p_uvn );
-            }
-            else {
-                tree[r]->parents.insert( tree[p.uri()] );
-            }
-        }
-    }
-
-    // make sure rdfs:Resource is visible by default
-    TypeVisibilityNode* rdfsResourceNode = 0;
-    QHash<QUrl, TypeVisibilityNode*>::iterator rdfsResourceIt = tree.find(Soprano::Vocabulary::RDFS::Resource());
-    if( rdfsResourceIt == tree.end() ) {
-        rdfsResourceNode = new TypeVisibilityNode(Soprano::Vocabulary::RDFS::Resource());
-        tree.insert( Soprano::Vocabulary::RDFS::Resource(), rdfsResourceNode );
-    }
-    else {
-        rdfsResourceNode = rdfsResourceIt.value();
-    }
-    if( rdfsResourceNode->userVisible == 0 ) {
-        rdfsResourceNode->userVisible = 1;
-    }
-    // add rdfs:Resource as parent for all top-level classes
-    for ( QHash<QUrl, TypeVisibilityNode*>::iterator it = tree.begin();
-          it != tree.end(); ++it ) {
-        if( it.value() != rdfsResourceNode && it.value()->parents.isEmpty() ) {
-            it.value()->parents.insert( rdfsResourceNode );
-        }
-    }
-
-    // finally determine visibility of all nodes
-    for ( QHash<QUrl, TypeVisibilityNode*>::iterator it = tree.begin();
-          it != tree.end(); ++it ) {
-        QSet<TypeVisibilityNode*> visitedNodes;
-        m_visibilityHash.insert(it.key(), it.value()->updateUserVisibility( visitedNodes ) == 1 );
-    }
-
-    // get rid of the temp tree
-    qDeleteAll(tree);
-}
-
-bool TypeVisibilityTree::isVisible(const QUrl &type) const
-{
-    QMutexLocker lock( &m_mutex );
-
-    QHash<QUrl, bool>::const_iterator it = m_visibilityHash.constFind(type);
-    if( it != m_visibilityHash.constEnd() ) {
-        return *it;
-    }
-    else {
-        kDebug() << "Could not find type" << type << "in tree. Defaulting to visible.";
-        return true;
-    }
-}
-
-QList<QUrl> TypeVisibilityTree::visibleTypes() const
-{
-    QList<QUrl> types;
-    for( QHash<QUrl, bool>::const_iterator it = m_visibilityHash.constBegin();
-         it != m_visibilityHash.constEnd(); ++it ) {
-        if( it.value() )
-            types << it.key();
-    }
-    return types;
-}
diff --git a/nepomuk/services/storage/typevisibilitytree.h b/nepomuk/services/storage/typevisibilitytree.h
deleted file mode 100644
index 41e4a2b..0000000
--- a/nepomuk/services/storage/typevisibilitytree.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef TYPEVISIBILITYTREE_H
-#define TYPEVISIBILITYTREE_H
-
-#include <QtCore/QUrl>
-#include <QtCore/QHash>
-#include <QtCore/QMutex>
-
-namespace Soprano {
-class Model;
-}
-
-/**
- * Builds a type tree that allows to check the nao:userVisible
- * value for each existing type.
- */
-class TypeVisibilityTree
-{
-public:
-    TypeVisibilityTree( Soprano::Model* model );
-    ~TypeVisibilityTree();
-
-    void rebuildTree();
-
-    /**
-     * Check if the specified type is visible.
-     */
-    bool isVisible( const QUrl& type ) const;
-
-    QList<QUrl> visibleTypes() const;
-
-private:
-    Soprano::Model* m_model;
-    class TypeVisibilityNode;
-    QHash<QUrl, bool> m_visibilityHash;
-    mutable QMutex m_mutex;
-};
-
-#endif // TYPEVISIBILITYTREE_H
diff --git a/nepomuk/services/strigi/CMakeLists.txt b/nepomuk/services/strigi/CMakeLists.txt
index ee550e2..ce8c3d1 100644
--- a/nepomuk/services/strigi/CMakeLists.txt
+++ b/nepomuk/services/strigi/CMakeLists.txt
@@ -8,44 +8,28 @@ include_directories(
   ${SOPRANO_INCLUDE_DIR}
   ${CMAKE_SOURCE_DIR}
   ${NEPOMUK_INCLUDE_DIR}
-  ${STRIGI_INCLUDE_DIR}
-  ${nepomukstrigiservice_BUILD_DIR}
+  ../storage/lib/
   )
 
-# Check if Strigi has the FileInputStream::open method which was introduced between versions 0.7.2 and 0.7.3
-include(CheckCXXSourceCompiles)
-set(CMAKE_REQUIRED_INCLUDES ${STRIGI_INCLUDE_DIR})
-set(CMAKE_REQUIRED_LIBRARIES ${STRIGI_STREAMS_LIBRARY})
-CHECK_CXX_SOURCE_COMPILES(
-  "#include <strigi/fileinputstream.h>\nint main(int ac,char* av[]) { Strigi::FileInputStream::open(\"dummy\"); }"
-  STRIGI_HAS_FILEINPUTSTREAM_OPEN
-)
-if(${STRIGI_HAS_FILEINPUTSTREAM_OPEN})
-  add_definitions(-DSTRIGI_HAS_FILEINPUTSTREAM_OPEN)
-endif(${STRIGI_HAS_FILEINPUTSTREAM_OPEN})
-
 set(strigiservice_SRCS
   strigiservice.cpp
   indexscheduler.cpp
+  indexcleaner.cpp
   strigiserviceconfig.cpp
   eventmonitor.cpp
-  nepomukindexwriter.cpp
-  nepomukindexfeeder.cpp
   util.cpp
   nepomukindexer.cpp
   )
 
 qt4_add_dbus_adaptor(strigiservice_SRCS ../../interfaces/org.kde.nepomuk.Strigi.xml strigiservice.h Nepomuk::StrigiService )
 
-qt4_add_dbus_interface(strigiservice_SRCS ../../interfaces/org.kde.nepomuk.RemovableStorage.xml removablestorageserviceinterface)
 qt4_add_dbus_interface(strigiservice_SRCS ../../interfaces/org.kde.nepomuk.FileWatch.xml filewatchserviceinterface)
 
 kde4_add_plugin(nepomukstrigiservice ${strigiservice_SRCS})
 
 target_link_libraries(nepomukstrigiservice
   nepomukcommon
-  ${STRIGI_STREAMANALYZER_LIBRARY}
-  ${STRIGI_STREAMS_LIBRARY}
+  nepomukdatamanagement
   ${KDE4_KDEUI_LIBS}
   ${KDE4_KIO_LIBS}
   ${KDE4_SOLID_LIBS}
@@ -66,4 +50,6 @@ install(
 install(
   TARGETS nepomukstrigiservice
   DESTINATION ${PLUGIN_INSTALL_DIR})
+
+add_subdirectory(indexer)
 # -----------------------------
diff --git a/nepomuk/services/strigi/eventmonitor.cpp b/nepomuk/services/strigi/eventmonitor.cpp
index 51ae599..621cb25 100644
--- a/nepomuk/services/strigi/eventmonitor.cpp
+++ b/nepomuk/services/strigi/eventmonitor.cpp
@@ -75,7 +75,7 @@ Nepomuk::EventMonitor::EventMonitor( IndexScheduler* scheduler, QObject* parent
         connect( m_indexScheduler, SIGNAL( indexingStopped() ),
                  this, SLOT( slotIndexingStopped() ),
                  Qt::QueuedConnection );
-                 
+
         connect( m_indexScheduler, SIGNAL( indexingSuspended(bool) ),
                  this, SLOT( slotIndexingSuspended(bool) ) );
     }
@@ -98,7 +98,6 @@ void Nepomuk::EventMonitor::slotPowerManagementStatusChanged( bool conserveResou
             sendEvent( "indexingResumed", i18n("Resuming indexing of files for fast searching."), "battery-charging" );
     }
     else if ( conserveResources &&
-              m_indexScheduler->isRunning() &&
               !m_indexScheduler->isSuspended() ) {
         kDebug() << "Pausing indexer due to power management";
         m_wasIndexingWhenPaused = m_indexScheduler->isIndexing();
@@ -114,8 +113,7 @@ void Nepomuk::EventMonitor::slotCheckAvailableSpace()
     KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( KStandardDirs::locateLocal( "data", "nepomuk/repository/", false ) );
     if ( info.isValid() ) {
         if ( info.available() <= StrigiServiceConfig::self()->minDiskSpace() ) {
-            if ( m_indexScheduler->isRunning() &&
-                !m_indexScheduler->isSuspended() ) {
+            if ( !m_indexScheduler->isSuspended() ) {
                 pauseIndexing( PausedDueToAvailSpace );
                 sendEvent( "indexingSuspended",
                            i18n("Disk space is running low (%1 left). Suspending indexing of files.",
@@ -142,7 +140,7 @@ void Nepomuk::EventMonitor::slotIndexingStopped()
     if ( !m_indexScheduler->isSuspended() ) {
         m_totalIndexingSeconds += m_indexingStartTime.secsTo( QDateTime::currentDateTime() );
         const int elapsed = m_totalIndexingSeconds * 1000;
-        
+
         kDebug() << "initial indexing took" << elapsed;
         sendEvent( "initialIndexingFinished",
                    i18nc( "@info %1 is a duration formatted using KLocale::prettyFormatDuration",
diff --git a/nepomuk/services/strigi/indexcleaner.cpp b/nepomuk/services/strigi/indexcleaner.cpp
new file mode 100644
index 0000000..a8bdeff
--- /dev/null
+++ b/nepomuk/services/strigi/indexcleaner.cpp
@@ -0,0 +1,331 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2010-2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "indexcleaner.h"
+#include "strigiserviceconfig.h"
+#include "util.h"
+
+#include <QtCore/QTimer>
+#include <QtCore/QMutexLocker>
+#include <KDebug>
+
+#include <Nepomuk/Resource>
+#include <Nepomuk/ResourceManager>
+
+#include <Soprano/Model>
+#include <Soprano/QueryResultIterator>
+#include <Soprano/NodeIterator>
+#include <Soprano/Node>
+
+#include <Soprano/Vocabulary/RDF>
+#include <Soprano/Vocabulary/Xesam>
+#include <Soprano/Vocabulary/NAO>
+#include <Nepomuk/Vocabulary/NFO>
+#include <Nepomuk/Vocabulary/NIE>
+
+using namespace Nepomuk::Vocabulary;
+using namespace Soprano::Vocabulary;
+
+
+Nepomuk::IndexCleaner::IndexCleaner(QObject* parent)
+    : KJob(parent),
+      m_delay(0)
+{
+    setCapabilities( Suspendable );
+}
+
+namespace {
+    /**
+     * Creates one SPARQL filter expression that excludes the include folders.
+     * This is necessary since constructFolderSubFilter will append a slash to
+     * each folder to make sure it does not match something like
+     * '/home/foobar' with '/home/foo'.
+     */
+    QString constructExcludeIncludeFoldersFilter()
+    {
+        QStringList filters;
+        foreach( const QString& folder, Nepomuk::StrigiServiceConfig::self()->includeFolders() ) {
+            filters << QString::fromLatin1( "(?url!=%1)" ).arg( Soprano::Node::resourceToN3( KUrl( folder ) ) );
+        }
+        return filters.join( QLatin1String( " && " ) );
+    }
+
+    QString constructFolderSubFilter( const QList<QPair<QString, bool> > folders, int& index )
+    {
+        QString path = folders[index].first;
+        if ( !path.endsWith( '/' ) )
+            path += '/';
+        const bool include = folders[index].second;
+
+        ++index;
+
+        QStringList subFilters;
+        while ( index < folders.count() &&
+                folders[index].first.startsWith( path ) ) {
+            subFilters << constructFolderSubFilter( folders, index );
+        }
+
+        QString thisFilter = QString::fromLatin1( "REGEX(STR(?url),'^%1')" ).arg( QString::fromAscii( KUrl( path ).toEncoded() ) );
+
+        // we want all folders that should NOT be indexed
+        if ( include ) {
+            thisFilter.prepend( '!' );
+        }
+        subFilters.prepend( thisFilter );
+
+        if ( subFilters.count() > 1 ) {
+            return '(' + subFilters.join( include ? QLatin1String( " || " ) : QLatin1String( " && " ) ) + ')';
+        }
+        else {
+            return subFilters.first();
+        }
+    }
+
+    /**
+     * Creates one SPARQL filter which matches all files and folders that should NOT be indexed.
+     */
+    QString constructFolderFilter()
+    {
+        QStringList subFilters( constructExcludeIncludeFoldersFilter() );
+
+        // now add the actual filters
+        QList<QPair<QString, bool> > folders = Nepomuk::StrigiServiceConfig::self()->folders();
+        int index = 0;
+        while ( index < folders.count() ) {
+            subFilters << constructFolderSubFilter( folders, index );
+        }
+        QString filters = subFilters.join(" && ");
+        if( !filters.isEmpty() )
+            return QString::fromLatin1("FILTER(%1) .").arg(filters);
+
+        return QString();
+    }
+}
+
+void Nepomuk::IndexCleaner::start()
+{
+    const QString folderFilter = constructFolderFilter();
+
+    const int limit = 20;
+
+    //
+    // Create all queries that return indexed data which should not be there anymore.
+    //
+
+    //
+    // Query the nepomukindexer app resource in order to speed up the queries.
+    //
+    QUrl appRes;
+    Soprano::QueryResultIterator appIt
+            = Nepomuk::ResourceManager::instance()->mainModel()->executeQuery(QString::fromLatin1("select ?app where { ?app %1 %2 . } LIMIT 1")
+                                                                              .arg(Soprano::Node::resourceToN3( NAO::identifier() ),
+                                                                                   Soprano::Node::literalToN3(QLatin1String("nepomukindexer"))),
+                                                                              Soprano::Query::QueryLanguageSparql);
+    if(appIt.next()) {
+        appRes = appIt[0].uri();
+    }
+
+    //
+    // 1. Data that has been created in KDE >= 4.7 using the DMS
+    //
+    if(!appRes.isEmpty()) {
+        m_removalQueries << QString::fromLatin1( "select distinct ?r where { "
+                                                 "graph ?g { ?r %1 ?url . } . "
+                                                 "?g %2 %3 . "
+                                                 " %4 } LIMIT %5" )
+                            .arg( Soprano::Node::resourceToN3( NIE::url() ),
+                                  Soprano::Node::resourceToN3( NAO::maintainedBy() ),
+                                  Soprano::Node::resourceToN3( appRes ),
+                                  folderFilter,
+                                  QString::number( limit ) );
+    }
+
+
+    //
+    // 2. (legacy data) We query all files that should not be in the store.
+    // This for example excludes all filex:/ URLs.
+    //
+    m_removalQueries << QString::fromLatin1( "select distinct ?r where { "
+                                             "?r %1 ?url . "
+                                             "?g <http://www.strigi.org/fields#indexGraphFor> ?r . "
+                                             "FILTER(REGEX(STR(?url),'^file:/')) . "
+                                             "%2 } LIMIT %3" )
+                        .arg( Soprano::Node::resourceToN3( NIE::url() ),
+                              folderFilter )
+                        .arg(limit);
+
+
+    //
+    // 3. Build filter query for all exclude filters
+    //
+    QStringList fileFilters;
+    foreach( const QString& filter, Nepomuk::StrigiServiceConfig::self()->excludeFilters() ) {
+        QString filterRxStr = QRegExp::escape( filter );
+        filterRxStr.replace( "\\*", QLatin1String( ".*" ) );
+        filterRxStr.replace( "\\?", QLatin1String( "." ) );
+        filterRxStr.replace( '\\',"\\\\" );
+        fileFilters << QString::fromLatin1( "REGEX(STR(?fn),\"^%1$\")" ).arg( filterRxStr );
+    }
+    const QString includeExcludeFilters = constructExcludeIncludeFoldersFilter();
+
+    if( !includeExcludeFilters.isEmpty() && !fileFilters.isEmpty() ) {
+        const QString filters = QString::fromLatin1("FILTER((%1) && (%2)) .").arg( includeExcludeFilters, fileFilters.join(" || ") );
+
+        // 3.1. Data for files which are excluded through filters
+        if(!appRes.isEmpty()) {
+            m_removalQueries << QString::fromLatin1( "select distinct ?r ?fn where { "
+                                                     "graph ?g { ?r %1 ?url . } . "
+                                                     "?r %2 ?fn . "
+                                                     "?g %3 %4 . "
+                                                     "FILTER(REGEX(STR(?url),\"^file:/\")) . "
+                                                     "%6 } LIMIT %7" )
+                                .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
+                                      Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NFO::fileName() ),
+                                      Soprano::Node::resourceToN3( NAO::maintainedBy() ),
+                                      Soprano::Node::resourceToN3( appRes ),
+                                      filters )
+                                .arg(limit);
+            m_excludeFilterRemovalQueries << m_removalQueries.last();
+        }
+
+        // 3.2. (legacy data) Data for files which are excluded through filters
+        m_removalQueries << QString::fromLatin1( "select distinct ?r ?fn where { "
+                                                 "?r %1 ?url . "
+                                                 "?r %2 ?fn . "
+                                                 "?g <http://www.strigi.org/fields#indexGraphFor> ?r . "
+                                                 "FILTER(REGEX(STR(?url),\"^file:/\")) . "
+                                                 "%3 } LIMIT %4" )
+                            .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
+                                  Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NFO::fileName() ),
+                                  filters )
+                            .arg(limit);
+        m_excludeFilterRemovalQueries << m_removalQueries.last();
+    }
+
+
+    //
+    // 4. (legacy data) Remove all old data from Xesam-times. While we leave out the data created by libnepomuk
+    // there is no problem since libnepomuk still uses backwards compatible queries and we use
+    // libnepomuk to determine URIs in the strigi backend.
+    //
+    m_removalQueries << QString::fromLatin1( "select distinct ?r where { "
+                                             "?r <http://strigi.sf.net/ontologies/0.9#parentUrl> ?p1 . } LIMIT %1" )
+                        .arg(limit);
+    m_removalQueries << QString::fromLatin1( "select distinct ?r where { "
+                                             "?r %1 ?u2 . } LIMIT %2" )
+                        .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::Xesam::url() ) )
+                        .arg(limit);
+
+
+    //
+    // 5. (legacy data) Remove data which is useless but still around from before. This could happen due to some buggy version of
+    // the indexer or the filewatch service or even some application messing up the data.
+    // We look for indexed files that do not have a nie:url defined and thus, will never be caught by any of the
+    // other queries. In addition we check for an isPartOf relation since strigi produces EmbeddedFileDataObjects
+    // for video and audio streams.
+    //
+    m_removalQueries << QString::fromLatin1("select ?r where { "
+                                            "graph ?g { ?r ?pp ?oo . } . "
+                                            "?g <http://www.strigi.org/fields#indexGraphFor> ?r . "
+                                            "FILTER(!bif:exists((select (1) where { ?r %1 ?u . }))) . "
+                                            "FILTER(!bif:exists((select (1) where { ?r %2 ?p . }))) . "
+                                            "} LIMIT %3")
+                        .arg(Soprano::Node::resourceToN3(NIE::url()),
+                             Soprano::Node::resourceToN3(NIE::isPartOf()))
+                        .arg(limit);
+
+    //
+    // Start the removal
+    //
+    m_query = m_removalQueries.dequeue();
+    clearNextBatch();
+}
+
+void Nepomuk::IndexCleaner::slotRemoveResourcesDone(KJob* job)
+{
+    if( job->error() ) {
+        kDebug() << job->errorString();
+    }
+
+    QMutexLocker lock(&m_stateMutex);
+    if( !m_suspended ) {
+        QTimer::singleShot(m_delay, this, SLOT(clearNextBatch()));
+    }
+}
+
+void Nepomuk::IndexCleaner::clearNextBatch()
+{
+    QList<QUrl> resources;
+    Soprano::QueryResultIterator it
+            = ResourceManager::instance()->mainModel()->executeQuery( m_query, Soprano::Query::QueryLanguageSparql );
+    while( it.next() ) {
+        //
+        // Workaround for a bug in Virtuoso 6.1.3 where file names with umlauts and
+        // accents always match
+        //
+        if(m_excludeFilterRemovalQueries.contains(m_query)) {
+            const QString fileName = it["fn"].toString();
+            if(StrigiServiceConfig::self()->shouldFileBeIndexed(fileName)) {
+                continue;
+            }
+        }
+        resources << it[0].uri();
+    }
+
+    if( !resources.isEmpty() ) {
+        KJob* job = Nepomuk::clearIndexedData(resources);
+        connect( job, SIGNAL(finished(KJob*)), this, SLOT(slotRemoveResourcesDone(KJob*)) );
+    }
+
+    else if( !m_removalQueries.isEmpty() ) {
+        m_query = m_removalQueries.dequeue();
+        clearNextBatch();
+    }
+
+    else {
+        emitResult();
+    }
+}
+
+bool Nepomuk::IndexCleaner::doSuspend()
+{
+    QMutexLocker locker(&m_stateMutex);
+    m_suspended = true;
+    return true;
+}
+
+bool Nepomuk::IndexCleaner::doResume()
+{
+    QMutexLocker locker(&m_stateMutex);
+    if(m_suspended) {
+        m_suspended = false;
+        QTimer::singleShot( 0, this, SLOT(clearNextBatch()) );
+    }
+    return true;
+}
+
+void Nepomuk::IndexCleaner::setDelay(int msecs)
+{
+    m_delay = msecs;
+}
+
+#include "indexcleaner.moc"
diff --git a/nepomuk/services/strigi/indexcleaner.h b/nepomuk/services/strigi/indexcleaner.h
new file mode 100644
index 0000000..459748a
--- /dev/null
+++ b/nepomuk/services/strigi/indexcleaner.h
@@ -0,0 +1,72 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+   Copyright (C) 2011 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INDEXCLEANER_H
+#define INDEXCLEANER_H
+
+#include <QtCore/QStringList>
+#include <QtCore/QQueue>
+#include <QtCore/QMutex>
+
+#include <KJob>
+
+namespace Nepomuk {
+    class IndexCleaner : public KJob
+    {
+        Q_OBJECT
+
+    public:
+        IndexCleaner(QObject* parent=0);
+
+        virtual void start();
+        virtual bool doSuspend();
+        virtual bool doResume();
+
+    public slots:
+        /**
+         * Set the delay between the cleanup queries.
+         * Used for throtteling the cleaner to not grab too
+         * many resources. Default is 0.
+         *
+         * \sa IndexScheduler::setIndexingSpeed()
+         */
+        void setDelay(int msecs);
+
+    private slots:
+        void clearNextBatch();
+        void slotRemoveResourcesDone(KJob* job);
+
+    private:
+        QQueue<QString> m_removalQueries;
+
+        QString m_query;
+
+        QMutex m_stateMutex;
+        bool m_suspended;
+        int m_delay;
+
+        /// the queries from m_removalQueries that handle exclude filters
+        QStringList m_excludeFilterRemovalQueries;
+    };
+}
+
+#endif // INDEXCLEANER_H
diff --git a/nepomuk/services/strigi/indexer/CMakeLists.txt b/nepomuk/services/strigi/indexer/CMakeLists.txt
new file mode 100644
index 0000000..5f94d8d
--- /dev/null
+++ b/nepomuk/services/strigi/indexer/CMakeLists.txt
@@ -0,0 +1,57 @@
+project(nepomukindexer)
+
+find_package(KDE4 REQUIRED)
+find_package(Nepomuk REQUIRED)
+
+include(KDE4Defaults)
+include(SopranoAddOntology)
+
+# Check if Strigi has the FileInputStream::open method which was introduced between versions 0.7.2 and 0.7.3
+include(CheckCXXSourceCompiles)
+set(CMAKE_REQUIRED_INCLUDES ${STRIGI_INCLUDE_DIR})
+set(CMAKE_REQUIRED_LIBRARIES ${STRIGI_STREAMS_LIBRARY})
+CHECK_CXX_SOURCE_COMPILES(
+  "#include <strigi/fileinputstream.h>\nint main(int ac,char* av[]) { Strigi::FileInputStream::open(\"dummy\"); }"
+  STRIGI_HAS_FILEINPUTSTREAM_OPEN
+)
+if(${STRIGI_HAS_FILEINPUTSTREAM_OPEN})
+  add_definitions(-DSTRIGI_HAS_FILEINPUTSTREAM_OPEN)
+endif(${STRIGI_HAS_FILEINPUTSTREAM_OPEN})
+
+include_directories(
+  ${QT_INCLUDES}
+  ${KDE4_INCLUDES}
+  ${SOPRANO_INCLUDE_DIR}
+  ${NEPOMUK_INCLUDE_DIR}
+  ${LIBSTREAMS_INCLUDE_DIRS}
+  ${LIBSTREAMANALYZER_INCLUDE_DIRS}
+  ../../storage/lib/
+  )
+
+set(indexer_SRCS
+  main.cpp
+  indexer.cpp
+  nepomukindexwriter.cpp
+  ../util.cpp
+  ../../../servicestub/priority.cpp
+  )
+
+soprano_add_ontology(indexer_SRCS ${nepomuk_ontologies_SOURCE_DIR}/kext.trig "KExt" "Nepomuk::Vocabulary" "trig")
+
+kde4_add_executable( nepomukindexer ${indexer_SRCS} )
+
+target_link_libraries( nepomukindexer
+  nepomukcommon
+  ${STRIGI_STREAMANALYZER_LIBRARY}
+  ${STRIGI_STREAMS_LIBRARY}
+  ${KDE4_KIO_LIBS}
+  ${NEPOMUK_QUERY_LIBRARIES}
+  ${NEPOMUK_LIBRARIES}
+  ${SOPRANO_LIBRARIES}
+  nepomukdatamanagement
+  )
+
+install(
+  TARGETS nepomukindexer
+  DESTINATION ${BIN_INSTALL_DIR} )
+# -----------------------------
diff --git a/nepomuk/services/strigi/indexer/indexer.cpp b/nepomuk/services/strigi/indexer/indexer.cpp
new file mode 100644
index 0000000..54dc8b4
--- /dev/null
+++ b/nepomuk/services/strigi/indexer/indexer.cpp
@@ -0,0 +1,158 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "indexer.h"
+#include "nepomukindexwriter.h"
+
+#include <Nepomuk/Vocabulary/NIE>
+
+#include <KDebug>
+
+#include <QtCore/QDataStream>
+#include <QtCore/QDateTime>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+
+#include <strigi/strigiconfig.h>
+#include <strigi/indexwriter.h>
+#include <strigi/analysisresult.h>
+#include <strigi/fileinputstream.h>
+#include <strigi/analyzerconfiguration.h>
+
+#include <iostream>
+
+
+namespace {
+    class StoppableConfiguration : public Strigi::AnalyzerConfiguration
+    {
+    public:
+        StoppableConfiguration()
+            : m_stop(false) {
+#if defined(STRIGI_IS_VERSION)
+#if STRIGI_IS_VERSION( 0, 6, 1 )
+            setIndexArchiveContents( false );
+#endif
+#endif
+        }
+
+        bool indexMore() const {
+            return !m_stop;
+        }
+
+        bool addMoreText() const {
+            return !m_stop;
+        }
+
+        void setStop( bool s ) {
+            m_stop = s;
+        }
+
+    private:
+        bool m_stop;
+    };
+}
+
+
+class Nepomuk::Indexer::Private
+{
+public:
+    StoppableConfiguration m_analyzerConfig;
+    StrigiIndexWriter* m_indexWriter;
+    Strigi::StreamAnalyzer* m_streamAnalyzer;
+};
+
+
+Nepomuk::Indexer::Indexer( QObject* parent )
+    : QObject( parent ),
+      d( new Private() )
+{
+    d->m_indexWriter = new StrigiIndexWriter();
+    d->m_streamAnalyzer = new Strigi::StreamAnalyzer( d->m_analyzerConfig );
+    d->m_streamAnalyzer->setIndexWriter( *d->m_indexWriter );
+}
+
+
+Nepomuk::Indexer::~Indexer()
+{
+    delete d->m_streamAnalyzer;
+    delete d->m_indexWriter;
+    delete d;
+}
+
+
+void Nepomuk::Indexer::indexFile( const KUrl& url, const KUrl resUri, uint mtime )
+{
+    indexFile( QFileInfo( url.toLocalFile() ), resUri, mtime );
+}
+
+
+void Nepomuk::Indexer::indexFile( const QFileInfo& info, const KUrl resUri, uint mtime )
+{
+    if( !info.exists() ) {
+        kDebug() << info.filePath() << " does not exist";
+        return;
+    }
+    
+    d->m_analyzerConfig.setStop( false );
+    d->m_indexWriter->forceUri( resUri );
+    
+    // strigi asserts if the file path has a trailing slash
+    const KUrl url( info.filePath() );
+    const QString filePath = url.toLocalFile( KUrl::RemoveTrailingSlash );
+    const QString dir = url.directory( KUrl::IgnoreTrailingSlash );
+
+    kDebug() << "Starting to analyze" << info.filePath();
+
+    Strigi::AnalysisResult analysisresult( QFile::encodeName( filePath ).data(),
+                                           mtime ? mtime : info.lastModified().toTime_t(),
+                                           *d->m_indexWriter,
+                                           *d->m_streamAnalyzer,
+                                           QFile::encodeName( dir ).data() );
+    if ( info.isFile() && !info.isSymLink() ) {
+#ifdef STRIGI_HAS_FILEINPUTSTREAM_OPEN
+        Strigi::InputStream* stream = Strigi::FileInputStream::open( QFile::encodeName( info.filePath() ) );
+        analysisresult.index( stream );
+        delete stream;
+#else
+        Strigi::FileInputStream stream( QFile::encodeName( info.filePath() ) );
+        analysisresult.index( &stream );
+#endif
+    }
+    else {
+        analysisresult.index(0);
+    }
+}
+
+void Nepomuk::Indexer::indexStdin(const KUrl resUri, uint mtime)
+{
+    d->m_analyzerConfig.setStop( false );
+    d->m_indexWriter->forceUri( resUri );
+
+    Strigi::AnalysisResult analysisresult( QFile::encodeName(resUri.fileName()).data(),
+                                           mtime ? mtime : QDateTime::currentDateTime().toTime_t(),
+                                           *d->m_indexWriter,
+                                           *d->m_streamAnalyzer );
+    Strigi::FileInputStream stream( stdin, QFile::encodeName(resUri.toLocalFile()).data() );
+    analysisresult.index( &stream );
+}
+
+#include "indexer.moc"
diff --git a/nepomuk/services/strigi/indexer/indexer.h b/nepomuk/services/strigi/indexer/indexer.h
new file mode 100644
index 0000000..4a604bd
--- /dev/null
+++ b/nepomuk/services/strigi/indexer/indexer.h
@@ -0,0 +1,77 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2011 Vishesh Handa <handa.vish@gmail.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NEPOMUK_STRIG_INDEXER_H_
+#define _NEPOMUK_STRIG_INDEXER_H_
+
+#include <QtCore/QObject>
+#include <KUrl>
+
+class QDateTime;
+class QDataStream;
+class QFileInfo;
+
+namespace Nepomuk {
+
+    class Resource;
+
+    class Indexer : public QObject
+    {
+        Q_OBJECT
+
+    public:
+        /**
+         * Create a new indexer.
+         */
+        Indexer( QObject* parent = 0 );
+
+        /**
+         * Destructor
+         */
+        ~Indexer();
+
+        /**
+         * Index a single local file or folder (files in a folder will
+         * not be indexed recursively).
+         */
+        void indexFile( const KUrl& url, const KUrl resUri, uint mtime = 0 );
+
+        /**
+         * Index a single local file or folder (files in a folder will
+         * not be indexed recursively). This method does the exact same
+         * as the above except that it saves an addditional stat of the
+         * file.
+         */
+        void indexFile( const QFileInfo& info, const KUrl resUri, uint mtime=0 );
+
+        /**
+         * Index a file whose contents are provided via standard input.
+         */
+        void indexStdin( const KUrl resUri, uint mtime=0 );
+        
+    private:
+        class Private;
+        Private* const d;
+    };
+}
+
+#endif
diff --git a/nepomuk/services/strigi/indexer/main.cpp b/nepomuk/services/strigi/indexer/main.cpp
new file mode 100644
index 0000000..72d373a
--- /dev/null
+++ b/nepomuk/services/strigi/indexer/main.cpp
@@ -0,0 +1,86 @@
+/*
+   This file is part of the Nepomuk KDE project.
+   Copyright (C) 2010-11 Vishesh Handa <handa.vish@gmail.com>
+   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) version 3, or any
+   later version accepted by the membership of KDE e.V. (or its
+   successor approved by the membership of KDE e.V.), which shall
+   act as a proxy defined in Section 6 of version 3 of the license.
+
+   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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "indexer.h"
+#include "../util.h"
+#include "../../../servicestub/priority.h"
+
+#include <KAboutData>
+#include <KCmdLineArgs>
+#include <KLocale>
+#include <KComponentData>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+
+#include <KDebug>
+#include <KUrl>
+
+int main(int argc, char *argv[])
+{
+    lowerIOPriority();
+    lowerSchedulingPriority();
+    lowerPriority();
+
+    KAboutData aboutData("nepomukindexer", 0, ki18n("NepomukIndexer"),
+                         "1.0",
+                         ki18n("NepomukIndexer indexes the contents of a file and saves the results in Nepomuk"),
+                         KAboutData::License_LGPL_V2,
+                         ki18n("(C) 2011, Vishesh Handa"));
+    aboutData.addAuthor(ki18n("Vishesh Handa"), ki18n("Current maintainer"), "handa.vish@gmail.com");
+    aboutData.addCredit(ki18n("Sebastian Trüg"), ki18n("Developer"), "trueg@kde.org");
+    
+    KCmdLineArgs::init(argc, argv, &aboutData);
+    
+    KCmdLineOptions options;
+    options.add("uri <uri>", ki18n("The URI provided will be forced on the resource"));
+    options.add("mtime <time>", ki18n("The modification time of the resource in time_t format"));
+    options.add("+[url]", ki18n("The URL of the file to be indexed"));
+    options.add("clear", ki18n("Remove all indexed data of the URL provided"));
+    
+    KCmdLineArgs::addCmdLineOptions(options);   
+    const KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+    // Application
+    QCoreApplication app( argc, argv );
+    KComponentData data( aboutData, KComponentData::RegisterAsMainComponent );
+    
+    const KUrl uri = args->getOption("uri");
+    const uint mtime = args->getOption("mtime").toUInt();
+
+    if( args->count() == 0 ) {
+        Nepomuk::Indexer indexer;
+        indexer.indexStdin( uri, mtime );
+        return 0;
+    }
+    else if( args->isSet("clear") ) {
+        Nepomuk::clearIndexedData( args->url(0) );
+        kDebug() << "Removed indexed data for" << args->url(0);
+        return 0;
+    }
+    else {
+        Nepomuk::Indexer indexer;
+        indexer.indexFile( args->url(0), uri, mtime );
+        kDebug() << "Indexed data for" << args->url(0);
+        return 0;
+    }
+}
diff --git a/nepomuk/services/strigi/indexer/nepomukindexwriter.cpp b/nepomuk/services/strigi/indexer/nepomukindexwriter.cpp
new file mode 100644
index 0000000..ce3cad7
--- /dev/null
+++ b/nepomuk/services/strigi/indexer/nepomukindexwriter.cpp
@@ -0,0 +1,566 @@
+/*
+  Copyright (C) 2007-2011 Sebastian Trueg <trueg@kde.org>
+  Copyright (C) 2010 Vishesh Handa <handa.vish@gmail.com>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU 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 library; see the file COPYING.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#include "nepomukindexwriter.h"
+#include "../util.h"
+#include "kext.h"
+#include "datamanagement.h"
+#include "simpleresource.h"
+#include "simpleresourcegraph.h"
+
+#include <Soprano/Vocabulary/RDF>
+#include <Soprano/Vocabulary/NAO>
+#include <Soprano/Vocabulary/NRL>
+#include <Soprano/LiteralValue>
+#include <Soprano/Node>
+#include <Soprano/QueryResultIterator>
+
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtCore/QVariant>
+#include <QtCore/QFileInfo>
+#include <QtCore/QFile>
+#include <QtCore/QUrl>
+#include <QtCore/QDateTime>
+#include <QtCore/QByteArray>
+#include <QtCore/QStack>
+#include <QtCore/QMutex>
+
+#include <KUrl>
+#include <KDebug>
+#include <KMimeType>
+#include <kde_file.h>
+#include <KJob>
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <map>
+#include <sstream>
+#include <algorithm>
+
+#include <Nepomuk/Vocabulary/NFO>
+#include <Nepomuk/Vocabulary/NIE>
+
+
+
+// IMPORTANT: strings in Strigi are apparently UTF8! Except for file names. Those are in local encoding.
+
+using namespace Soprano;
+using namespace Strigi;
+using namespace Soprano::Vocabulary;
+using namespace Nepomuk::Vocabulary;
+
+
+uint qHash( const std::string& s )
+{
+    return qHash( s.c_str() );
+}
+
+namespace {
+    QString findArchivePath( const QString& path ) {
+        QString p( path );
+        int i = 0;
+        while ( ( i = p.lastIndexOf( '/' ) ) > 0 ) {
+            p.truncate( i );
+            if ( QFileInfo( p ).isFile() ) {
+                return p;
+            }
+        }
+        return QString();
+    }
+
+    QUrl createFileUrl( const Strigi::AnalysisResult* idx ) {
+        // HACK: Strigi includes analysers that recurse into tar or zip archives and index
+        // the files therein. In KDE these files could perfectly be handled through kio slaves
+        // such as tar:/ or zip:/
+        // Here we try to use KDE-compatible URIs for these indexed files the best we can
+        // everything else defaults to file:/
+        QUrl uri;
+        QString path = QFile::decodeName( idx->path().c_str() );
+        if ( KUrl::isRelativeUrl( path ) )
+            uri = QUrl::fromLocalFile( QFileInfo( path ).absoluteFilePath() );
+        else
+            uri = KUrl( path ); // try to support http and other URLs
+
+        if ( idx->depth() > 0 ) {
+            QString archivePath = findArchivePath( path );
+            if ( QFile::exists( archivePath ) ) {
+                if ( archivePath.endsWith( QLatin1String( ".tar" ) ) ||
+                     archivePath.endsWith( QLatin1String( ".tar.gz" ) ) ||
+                     archivePath.endsWith( QLatin1String( ".tar.bz2" ) ) ||
+                     archivePath.endsWith( QLatin1String( ".tar.lzma" ) ) ||
+                     archivePath.endsWith( QLatin1String( ".tar.xz" ) ) ) {
+                    uri.setScheme( "tar" );
+                }
+                else if ( archivePath.endsWith( QLatin1String( ".zip" ) ) ) {
+                    uri.setScheme( "zip" );
+                }
+            }
+        }
+
+        // fallback for all
+        if ( uri.scheme().isEmpty() ) {
+            uri.setScheme( "file" );
+        }
+
+        return uri;
+    }
+
+
+    /**
+     * A simple cache for properties that are used in Strigi.
+     * This avoids matching them again and again.
+     *
+     * Also the class provides easy conversion methods for
+     * values provided by Strigi to values that Nepomuk understands.
+     */
+    class RegisteredFieldData
+    {
+    public:
+        RegisteredFieldData( const QUrl& prop )
+            : m_property( prop ) {
+        }
+
+        QUrl property() const { return m_property; }
+
+    private:
+        /// The actual property URI
+        QUrl m_property;
+    };
+
+
+    /**
+     * Data objects that are used to store information relative to one
+     * indexing run.
+     */
+    class FileMetaData
+    {
+    public:
+        FileMetaData( const Strigi::AnalysisResult* idx, const QUrl & resUri );
+
+        /// convert the Strigi ids found in object values into the corresponding sub-resource URIs
+        Nepomuk::SimpleResource convertSubResourceIds(const Nepomuk::SimpleResource& res) const;
+
+        /// The resource URI
+        QUrl resourceUri;
+
+        /// The file URL (nie:url)
+        KUrl fileUrl;
+
+        /// The file info - saved to prevent multiple stats
+        QFileInfo fileInfo;
+
+        /// a buffer for all plain-text content generated by strigi
+        std::string content;
+
+        /// The main file's metadata
+        Nepomuk::SimpleResource data;
+
+        /// The sub-resources, mapped by the identifier libstreamanalyzer provided
+        QHash<QString, Nepomuk::SimpleResource> subResources;
+
+    private:
+        /// The Strigi result
+        const Strigi::AnalysisResult* m_analysisResult;
+    };
+
+
+    FileMetaData::FileMetaData( const Strigi::AnalysisResult* idx, const QUrl & resUri )
+        : m_analysisResult( idx )
+    {
+        fileUrl = createFileUrl( idx );
+        fileInfo = QFileInfo( fileUrl.toLocalFile() );
+        resourceUri = resUri;
+        if(resourceUri.isEmpty()) {
+            data.setUri(fileUrl);
+        }
+        else {
+            data.setUri(resourceUri);
+        }
+    }
+
+    Nepomuk::SimpleResource FileMetaData::convertSubResourceIds(const Nepomuk::SimpleResource& res) const
+    {
+        Nepomuk::PropertyHash props = res.properties();
+        QMutableHashIterator<QUrl, QVariant> it(props);
+        while(it.hasNext()) {
+            it.next();
+            if(it.value().type() == QVariant::String) {
+                QHash<QString, Nepomuk::SimpleResource>::const_iterator subResIt = subResources.constFind(it.value().toString());
+                if(subResIt != subResources.constEnd()) {
+                    it.setValue(subResIt.value().uri());
+                }
+            }
+        }
+        Nepomuk::SimpleResource newRes(res);
+        newRes.setProperties(props);
+        return newRes;
+    }
+
+    FileMetaData* fileDataForResult( const Strigi::AnalysisResult* idx )
+    {
+        return static_cast<FileMetaData*>( idx->writerData() );
+    }
+}
+
+
+class Nepomuk::StrigiIndexWriter::Private
+{
+public:
+    //
+    // The Strigi API does not provide context information in addTriplet, i.e. the AnalysisResult.
+    // However, we only use one thread, only one AnalysisResult at the time.
+    // Thus, we can just remember that and use it in addTriplet.
+    //
+    QMutex resultStackMutex;
+    QStack<const Strigi::AnalysisResult*> currentResultStack;
+
+    // Some services may need to force a specific resource uri
+    QUrl resourceUri;
+};
+
+
+Nepomuk::StrigiIndexWriter::StrigiIndexWriter()
+    : Strigi::IndexWriter(),
+    d( new Private() )
+{
+}
+
+
+Nepomuk::StrigiIndexWriter::~StrigiIndexWriter()
+{
+    kDebug();
+    delete d;
+}
+
+
+// unused
+void Nepomuk::StrigiIndexWriter::commit()
+{
+    // do nothing
+    Q_ASSERT(false);
+}
+
+
+// unused
+void Nepomuk::StrigiIndexWriter::deleteEntries( const std::vector<std::string>& entries )
+{
+    // do nothing
+    Q_UNUSED(entries);
+    Q_ASSERT(false);
+}
+
+
+// unused
+void Nepomuk::StrigiIndexWriter::deleteAllEntries()
+{
+    // do nothing
+    Q_ASSERT(false);
+}
+
+
+// called for each indexed file
+void Nepomuk::StrigiIndexWriter::startAnalysis( const AnalysisResult* idx )
+{
+    // we need to remember the AnalysisResult since addTriplet does not provide it
+    d->currentResultStack.push(idx);
+
+    // for now we ignore embedded files -> too many false positives and useless query results
+    if ( idx->depth() > 0 ) {
+        return;
+    }
+
+    // create the file data used during the analysis
+    FileMetaData* data = new FileMetaData( idx, d->resourceUri );
+
+    // remove previously indexed data
+    // but only for files. We cannot remove folders since that would also remove the
+    // nie:isPartOf links from the files.
+    if(!data->fileInfo.isDir()) {
+        if( !data->resourceUri.isEmpty() ) {
+            Nepomuk::clearIndexedData(data->resourceUri)->exec();
+        }
+        else {
+            Nepomuk::clearIndexedData(data->fileUrl)->exec();
+        }
+    }
+
+    // remember the file data
+    idx->setWriterData( data );
+}
+
+
+void Nepomuk::StrigiIndexWriter::addText( const AnalysisResult* idx, const char* text, int32_t length )
+{
+    if ( idx->depth() > 0 ) {
+        return;
+    }
+
+    FileMetaData* md = fileDataForResult( idx );
+
+    // make sure text fragments are separated
+    if(md->content.size() > 0)
+        md->content.append( " " );
+
+    // append the new fragment
+    md->content.append( text, length );
+}
+
+
+void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
+                                           const RegisteredField* field,
+                                           const std::string& value )
+{
+    if ( idx->depth() > 0 ) {
+        return;
+    }
+
+    if ( value.length() > 0 ) {
+        FileMetaData* md = fileDataForResult( idx );
+        RegisteredFieldData* rfd = reinterpret_cast<RegisteredFieldData*>( field->writerData() );
+
+        //
+        // ignore the path as we handle that ourselves in startAnalysis
+        // ignore the mimetype since we handle that ourselves in finishAnalysis
+        //  and KDE's mimetype is much more reliable than Strigi's.
+        // TODO: remember the mimetype and use it only if KMimeType does not find one.
+        //
+        if ( field->key() != FieldRegister::pathFieldName &&
+             field->key() != FieldRegister::mimetypeFieldName ) {
+            md->data.addProperty(rfd->property(), QString::fromUtf8(value.c_str()));
+        }
+    }
+}
+
+
+// the main addValue method
+void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
+                                           const RegisteredField* field,
+                                           const unsigned char* data,
+                                           uint32_t size )
+{
+    addValue( idx, field, std::string( ( const char* )data, size ) );
+}
+
+
+void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult*, const RegisteredField*,
+                                           const std::string&, const std::string& )
+{
+    // we do not support map types
+}
+
+
+void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
+                                           const RegisteredField* field,
+                                           uint32_t value )
+{
+    if ( idx->depth() > 0 ) {
+        return;
+    }
+
+    FileMetaData* md = fileDataForResult( idx );
+    RegisteredFieldData* rfd = reinterpret_cast<RegisteredFieldData*>( field->writerData() );
+
+    md->data.addProperty(rfd->property(), value);
+}
+
+
+void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
+                                           const RegisteredField* field,
+                                           int32_t value )
+{
+    if ( idx->depth() > 0 ) {
+        return;
+    }
+
+    FileMetaData* md = fileDataForResult( idx );
+    RegisteredFieldData* rfd = reinterpret_cast<RegisteredFieldData*>( field->writerData() );
+
+    md->data.addProperty(rfd->property(), value);
+}
+
+
+void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
+                                           const RegisteredField* field,
+                                           double value )
+{
+    if ( idx->depth() > 0 ) {
+        return;
+    }
+
+    FileMetaData* md = fileDataForResult( idx );
+    RegisteredFieldData* rfd = reinterpret_cast<RegisteredFieldData*>( field->writerData() );
+
+    md->data.addProperty(rfd->property(), value);
+}
+
+
+void Nepomuk::StrigiIndexWriter::addTriplet( const std::string& s,
+                                             const std::string& p,
+                                             const std::string& o )
+{
+    if ( d->currentResultStack.top()->depth() > 0 ) {
+        kDebug() << "Depth > 0 -" << s.c_str() << p.c_str() << o.c_str();
+        return;
+    }
+    FileMetaData* md = fileDataForResult( d->currentResultStack.top() );
+
+    // convert the std strings to Qt values
+    const QString subResId(QString::fromUtf8(s.c_str()));
+    const QUrl property(QString::fromUtf8(p.c_str()));
+    const QString value(QString::fromUtf8(o.c_str()));
+
+    // the subject might be the indexed file itself
+    if(KUrl(subResId) == md->fileUrl) {
+        md->data.addProperty(property, value);
+    }
+    else {
+        // Find the corresponding sub-resource
+        QHash<QString, Nepomuk::SimpleResource>::iterator it = md->subResources.find(subResId);
+        if(it == md->subResources.end()) {
+            Nepomuk::SimpleResource subRes;
+            subRes.addProperty(property, value);
+            md->subResources.insert(subResId, subRes);
+        }
+        else {
+            it->addProperty(property, value);
+        }
+    }
+}
+
+
+// called after each indexed file
+void Nepomuk::StrigiIndexWriter::finishAnalysis( const AnalysisResult* idx )
+{
+    d->currentResultStack.pop();
+
+    if ( idx->depth() > 0 ) {
+        return;
+    }
+
+    FileMetaData* md = fileDataForResult( idx );
+
+    // store the full text of the file
+    if ( md->content.length() > 0 ) {
+        md->data.addProperty(Nepomuk::Vocabulary::NIE::plainTextContent(),
+                             QString::fromUtf8( md->content.c_str() ));
+    }
+
+    // store the nie:url in any case
+    md->data.addProperty(Nepomuk::Vocabulary::NIE::url(), md->fileUrl);
+
+    if( md->fileInfo.exists() ) {
+        //
+        // Strigi only indexes files and many extractors are still not perfect with types.
+        // Each file is a nie:DataObject and a nie:InformationElement. Many Strigi plugins
+        // forget at least one of the two.
+        // Thus, here we go the easy way and mark each indexed file as a nfo:FileDataObject
+        // and a nie:InformationElement, thus, at least providing the basic types to Nepomuk's
+        // domain and range checking.
+        //
+        md->data.addType(Nepomuk::Vocabulary::NFO::FileDataObject());
+        md->data.addType(Nepomuk::Vocabulary::NIE::InformationElement());
+        if ( md->fileInfo.isDir() ) {
+            md->data.addType(Nepomuk::Vocabulary::NFO::Folder());
+        }
+
+        // write the mimetype for local files
+        md->data.addProperty( Nepomuk::Vocabulary::NIE::mimeType(),
+                              KMimeType::findByUrl(md->fileUrl)->name() );
+#ifdef Q_OS_UNIX
+        KDE_struct_stat statBuf;
+        if( KDE_stat( QFile::encodeName(md->fileInfo.absoluteFilePath()).data(), &statBuf ) == 0 ) {
+            md->data.addProperty( Nepomuk::Vocabulary::KExt::unixFileMode(),
+                                  int(statBuf.st_mode) );
+        }
+        if( !md->fileInfo.owner().isEmpty() )
+            md->data.addProperty( Nepomuk::Vocabulary::KExt::unixFileOwner(),
+                                  md->fileInfo.owner() );
+        if( !md->fileInfo.group().isEmpty() )
+            md->data.addProperty( Nepomuk::Vocabulary::KExt::unixFileGroup(),
+                                  md->fileInfo.group() );
+#endif // Q_OS_UNIX
+    }
+
+
+    //
+    // Finally push all the information to Nepomuk
+    //
+    Nepomuk::SimpleResourceGraph graph;
+
+    // Add all the sub-resources as sub-resources of the main resource
+    for(QHash<QString, Nepomuk::SimpleResource>::const_iterator it = md->subResources.constBegin();
+        it != md->subResources.constEnd(); ++it) {
+        md->data.addProperty(NAO::hasSubResource(), it.value());
+        graph << md->convertSubResourceIds(it.value());
+    }
+
+    // finally add the file resource itself to the graph
+    graph << md->convertSubResourceIds(md->data);
+
+    // Add additional metadata - mark the information as discardable
+    QHash<QUrl, QVariant> additionalMetadata;
+    additionalMetadata.insert( RDF::type(), NRL::DiscardableInstanceBase() );
+
+    // we do not have an event loop - thus, we need to delete the job ourselves
+    QScopedPointer<KJob> job( Nepomuk::storeResources( graph, Nepomuk::IdentifyNew, Nepomuk::LazyCardinalities|Nepomuk::OverwriteProperties, additionalMetadata ) );
+    job->setAutoDelete(false);
+    job->exec();
+    if( job->error() ) {
+        kDebug() << job->errorString();
+    }
+
+    // cleanup
+    delete md;
+    idx->setWriterData( 0 );
+}
+
+
+void Nepomuk::StrigiIndexWriter::initWriterData( const Strigi::FieldRegister& f )
+{
+    // cache type conversion for all strigi fields
+    std::map<std::string, RegisteredField*>::const_iterator i;
+    std::map<std::string, RegisteredField*>::const_iterator end = f.fields().end();
+    for (i = f.fields().begin(); i != end; ++i) {
+        const QUrl prop = QUrl::fromEncoded( i->second->key().c_str() );
+        i->second->setWriterData( new RegisteredFieldData( prop ) );
+    }
+}
+
+
+void Nepomuk::StrigiIndexWriter::releaseWriterData( const Strigi::FieldRegister& f )
+{
+    std::map<std::string, RegisteredField*>::const_iterator i;
+    std::map<std::string, RegisteredField*>::const_iterator end = f.fields().end();
+    for (i = f.fields().begin(); i != end; ++i) {
+        delete static_cast<RegisteredFieldData*>( i->second->writerData() );
+        i->second->setWriterData( 0 );
+    }
+}
+
+
+void Nepomuk::StrigiIndexWriter::forceUri(const QUrl& uri)
+{
+    d->resourceUri = uri;
+}
diff --git a/nepomuk/services/strigi/indexer/nepomukindexwriter.h b/nepomuk/services/strigi/indexer/nepomukindexwriter.h
new file mode 100644
index 0000000..75cadbb
--- /dev/null
+++ b/nepomuk/services/strigi/indexer/nepomukindexwriter.h
@@ -0,0 +1,85 @@
+/*
+  Copyright (C) 2007-2010 Sebastian Trueg <trueg@kde.org>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU 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 library; see the file COPYING.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _STRIGI_NEPOMUK_INDEX_WRITER_H_
+#define _STRIGI_NEPOMUK_INDEX_WRITER_H_
+
+#include <strigi/indexwriter.h>
+#include <strigi/analysisresult.h>
+#include <strigi/analyzerconfiguration.h>
+
+class KUrl;
+class QUrl;
+
+namespace Nepomuk {
+
+    class Resource;
+
+    class StrigiIndexWriter : public Strigi::IndexWriter
+    {
+    public:
+        StrigiIndexWriter();
+        ~StrigiIndexWriter();
+
+        void commit();
+
+        /**
+         * Delete the entries with the given paths from the index.
+         *
+         * @param entries the paths of the files that should be deleted
+         **/
+        void deleteEntries( const std::vector<std::string>& entries );
+
+        /**
+         * Delete all indexed documents from the index.
+         **/
+        void deleteAllEntries();
+
+        void initWriterData( const Strigi::FieldRegister& );
+        void releaseWriterData( const Strigi::FieldRegister& );
+
+        void startAnalysis( const Strigi::AnalysisResult* );
+        void addText( const Strigi::AnalysisResult*, const char* text, int32_t length );
+        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
+                       const std::string& value );
+        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
+                       const unsigned char* data, uint32_t size );
+        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
+                       int32_t value );
+        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
+                       uint32_t value );
+        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
+                       double value );
+        void addTriplet( const std::string& subject,
+                         const std::string& predicate, const std::string& object );
+        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
+                       const std::string& name, const std::string& value );
+        void finishAnalysis( const Strigi::AnalysisResult* );
+
+        void forceUri( const QUrl & uri );
+
+    private:
+        class Private;
+        Private* d;
+    };
+}
+
+uint qHash( const std::string& s );
+
+#endif
diff --git a/nepomuk/services/strigi/indexscheduler.cpp b/nepomuk/services/strigi/indexscheduler.cpp
index 22b599d..8a051e3 100644
--- a/nepomuk/services/strigi/indexscheduler.cpp
+++ b/nepomuk/services/strigi/indexscheduler.cpp
@@ -1,6 +1,6 @@
 /* This file is part of the KDE Project
    Copyright (c) 2008-2010 Sebastian Trueg <trueg@kde.org>
-   Copyright (c) 2010 Vishesh Handa <handa.vish@gmail.com>
+   Copyright (c) 2010-11 Vishesh Handa <handa.vish@gmail.com>
 
    Parts of this file are based on code from Strigi
    Copyright (C) 2006-2007 Jos van den Oever <jos@vandenoever.info>
@@ -24,6 +24,7 @@
 #include "strigiserviceconfig.h"
 #include "nepomukindexer.h"
 #include "util.h"
+#include "datamanagement.h"
 
 #include <QtCore/QMutexLocker>
 #include <QtCore/QList>
@@ -41,8 +42,6 @@
 #include <Nepomuk/Resource>
 #include <Nepomuk/ResourceManager>
 #include <Nepomuk/Variant>
-#include <Nepomuk/Query/Query>
-#include <Nepomuk/Query/ComparisonTerm>
 #include <Nepomuk/Query/ResourceTerm>
 
 #include <Soprano/Model>
@@ -51,52 +50,31 @@
 #include <Soprano/Node>
 
 #include <Soprano/Vocabulary/RDF>
-#include <Soprano/Vocabulary/Xesam>
-#include <Nepomuk/Vocabulary/NFO>
 #include <Nepomuk/Vocabulary/NIE>
 
 #include <map>
 #include <vector>
+#include "indexcleaner.h"
 
+using namespace Soprano::Vocabulary;
+using namespace Nepomuk::Vocabulary;
 
 namespace {
     const int s_reducedSpeedDelay = 500; // ms
     const int s_snailPaceDelay = 3000;   // ms
 
-
-    bool isResourcePresent( const QString & dir ) {
-        QString query = QString::fromLatin1(" ask { ?r %1 %2. } ")
-                        .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                              Soprano::Node::resourceToN3( KUrl( dir ) ) );
-        return Nepomuk::ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql ).boolValue();
-    }
-
     QHash<QString, QDateTime> getChildren( const QString& dir )
     {
         QHash<QString, QDateTime> children;
-        QString query;
-
-        if( !isResourcePresent( dir ) ) {
-            query = QString::fromLatin1( "select distinct ?url ?mtime where { "
-                                         "?r %1 ?url . "
-                                         "FILTER( regex(str(?url), '^file://%2/([^/]*)$') ) . "
-                                         "?r %3 ?mtime ."
-                                         "}" )
-                    .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                          dir,
-                          Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::lastModified() ) );
-        }
-        else {
-            query = QString::fromLatin1( "select distinct ?url ?mtime where { "
-                                        "?r %1 ?parent . ?parent %2 %3 . "
-                                        "?r %4 ?mtime . "
-                                        "?r %2 ?url . "
-                                        "}" )
-                    .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::isPartOf() ),
-                        Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                        Soprano::Node::resourceToN3( KUrl( dir ) ),
-                        Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::lastModified() ) );
-        }
+        QString query = QString::fromLatin1( "select distinct ?url ?mtime where { "
+                                             "?r %1 ?parent . ?parent %2 %3 . "
+                                             "?r %4 ?mtime . "
+                                             "?r %2 ?url . "
+                                             "}" )
+                .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::isPartOf() ),
+                      Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
+                      Soprano::Node::resourceToN3( KUrl( dir ) ),
+                      Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::lastModified() ) );
         //kDebug() << "running getChildren query:" << query;
 
         Soprano::QueryResultIterator result = Nepomuk::ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
@@ -107,6 +85,31 @@ namespace {
 
         return children;
     }
+
+    QDateTime indexedMTimeForUrl(const KUrl& url)
+    {
+        Soprano::QueryResultIterator it
+                = Nepomuk::ResourceManager::instance()->mainModel()->executeQuery(QString::fromLatin1("select ?mt where { ?r %1 %2 . ?r %3 ?mt . } LIMIT 1")
+                                                                                  .arg(Soprano::Node::resourceToN3(NIE::url()),
+                                                                                       Soprano::Node::resourceToN3(url),
+                                                                                       Soprano::Node::resourceToN3(NIE::lastModified())),
+                                                                                  Soprano::Query::QueryLanguageSparql);
+        if(it.next()) {
+            return it[0].literal().toDateTime();
+        }
+        else {
+            return QDateTime();
+        }
+    }
+
+    bool compareIndexedMTime(const KUrl& url, const QDateTime& mtime)
+    {
+        const QDateTime indexedMTime = indexedMTimeForUrl(url);
+        if(indexedMTime.isNull())
+            return false;
+        else
+            return indexedMTime == mtime;
+    }
 }
 
 
@@ -158,15 +161,21 @@ void Nepomuk::IndexScheduler::UpdateDirQueue::clearByFlags( UpdateDirFlags mask
 
 
 Nepomuk::IndexScheduler::IndexScheduler( QObject* parent )
-    : QThread( parent ),
+    : QObject( parent ),
       m_suspended( false ),
-      m_stopped( false ),
       m_indexing( false ),
-      m_speed( FullSpeed )
+      m_indexingDelay( 0 )
 {
-    m_indexer = new Nepomuk::Indexer( this );
+    m_cleaner = new IndexCleaner(this);
+    connect( m_cleaner, SIGNAL(finished(KJob*)), this, SLOT(slotCleaningDone()) );
+    m_cleaner->start();
+
     connect( StrigiServiceConfig::self(), SIGNAL( configChanged() ),
              this, SLOT( slotConfigChanged() ) );
+
+    // start the initial indexing
+    queueAllFoldersForUpdate();
+    callDoIndexing();
 }
 
 
@@ -177,9 +186,12 @@ Nepomuk::IndexScheduler::~IndexScheduler()
 
 void Nepomuk::IndexScheduler::suspend()
 {
-    if ( isRunning() && !m_suspended ) {
-        QMutexLocker locker( &m_resumeStopMutex );
+    QMutexLocker locker( &m_suspendMutex );
+    if ( !m_suspended ) {
         m_suspended = true;
+        if( m_cleaner ) {
+            m_cleaner->suspend();
+        }
         emit indexingSuspended( true );
     }
 }
@@ -187,10 +199,16 @@ void Nepomuk::IndexScheduler::suspend()
 
 void Nepomuk::IndexScheduler::resume()
 {
-    if ( isRunning() && m_suspended ) {
-        QMutexLocker locker( &m_resumeStopMutex );
+    QMutexLocker locker( &m_suspendMutex );
+    if ( m_suspended ) {
         m_suspended = false;
-        m_resumeStopWc.wakeAll();
+
+        if( m_cleaner ) {
+            m_cleaner->resume();
+        }
+
+        callDoIndexing();
+
         emit indexingSuspended( false );
     }
 }
@@ -205,31 +223,16 @@ void Nepomuk::IndexScheduler::setSuspended( bool suspended )
 }
 
 
-void Nepomuk::IndexScheduler::stop()
-{
-    if ( isRunning() ) {
-        QMutexLocker locker( &m_resumeStopMutex );
-        m_stopped = true;
-        m_suspended = false;
-        m_indexer->stop();
-        m_dirsToUpdateWc.wakeAll();
-        m_resumeStopWc.wakeAll();
-    }
-}
-
-
-void Nepomuk::IndexScheduler::restart()
-{
-    stop();
-    wait();
-    start();
-}
-
-
 void Nepomuk::IndexScheduler::setIndexingSpeed( IndexingSpeed speed )
 {
     kDebug() << speed;
-    m_speed = speed;
+    m_indexingDelay = 0;
+    if ( speed != FullSpeed ) {
+        m_indexingDelay = (speed == ReducedSpeed) ? s_reducedSpeedDelay : s_snailPaceDelay;
+    }
+    if( m_cleaner ) {
+        m_cleaner->setDelay(m_indexingDelay);
+    }
 }
 
 
@@ -244,30 +247,36 @@ void Nepomuk::IndexScheduler::setReducedIndexingSpeed( bool reduced )
 
 bool Nepomuk::IndexScheduler::isSuspended() const
 {
-    return isRunning() && m_suspended;
+    QMutexLocker locker( &m_suspendMutex );
+    return m_suspended;
 }
 
 
 bool Nepomuk::IndexScheduler::isIndexing() const
 {
+    QMutexLocker locker( &m_indexingMutex );
     return m_indexing;
 }
 
 
 QString Nepomuk::IndexScheduler::currentFolder() const
 {
-    return m_currentFolder;
+    QMutexLocker locker( &m_currentMutex );
+    return m_currentUrl.directory();
 }
 
 
 QString Nepomuk::IndexScheduler::currentFile() const
 {
+    QMutexLocker locker( &m_currentMutex );
     return m_currentUrl.toLocalFile();
 }
 
 
 void Nepomuk::IndexScheduler::setIndexingStarted( bool started )
 {
+    QMutexLocker locker( &m_indexingMutex );
+
     if ( started != m_indexing ) {
         m_indexing = started;
         emit indexingStateChanged( m_indexing );
@@ -279,78 +288,70 @@ void Nepomuk::IndexScheduler::setIndexingStarted( bool started )
 }
 
 
-void Nepomuk::IndexScheduler::run()
+void Nepomuk::IndexScheduler::slotCleaningDone()
 {
-    // set lowest priority for this thread
-    setPriority( QThread::IdlePriority );
-    m_indexer->start();
-    
-    setIndexingStarted( true );
-
-    // initialization
-    queueAllFoldersForUpdate();
-
-#ifndef NDEBUG
-    QTime timer;
-    timer.start();
-#endif
+    m_cleaner = 0;
+}
 
-    removeOldAndUnwantedEntries();
+void Nepomuk::IndexScheduler::doIndexing()
+{
+    setIndexingStarted( true );
 
-#ifndef NDEBUG
-    kDebug() << "Removed old entries: " << timer.elapsed()/1000.0 << "secs";
-    timer.restart();
-#endif
+    // lock file and dir queues as we check their status
+    QMutexLocker fileLock( &m_filesToUpdateMutex );
+    QMutexLocker dirLock( &m_dirsToUpdateMutex );
 
-    while ( waitForContinue(true) ) {
-        // wait for more dirs to analyze in case the initial
-        // indexing is done
-        m_dirsToUpdateMutex.lock();
-        if ( m_dirsToUpdate.isEmpty() ) {
-            setIndexingStarted( false );
+    // get the next file
+    if( !m_filesToUpdate.isEmpty() ) {
+        kDebug() << "File";
 
-#ifndef NDEBUG
-            kDebug() << "All folders updated: " << timer.elapsed()/1000.0 << "sec";
-#endif
+        QFileInfo file = m_filesToUpdate.dequeue();
 
-            m_dirsToUpdateWc.wait( &m_dirsToUpdateMutex );
+        m_currentMutex.lock();
+        m_currentUrl = file.filePath();
+        m_currentMutex.unlock();
+        emit indexingFile( currentFile() );
 
-#ifndef NDEBUG
-            timer.restart();
-#endif
+        KJob * indexer = new Indexer( file );
+        connect( indexer, SIGNAL(finished(KJob*)), this, SLOT(slotIndexingDone(KJob*)) );
+        indexer->start();
+    }
 
-            if ( !m_stopped )
-                setIndexingStarted( true );
-        }
-        m_dirsToUpdateMutex.unlock();
+    // get the next folder
+    else if( !m_dirsToUpdate.isEmpty() ) {
+        kDebug() << "Directory";
 
-        // wait for resume or stop (or simply continue)
-        if ( !waitForContinue() ) {
-            break;
-        }
-
-        // get the next folder
-        m_dirsToUpdateMutex.lock();
         QPair<QString, UpdateDirFlags> dir = m_dirsToUpdate.dequeue();
-        m_dirsToUpdateMutex.unlock();
+        dirLock.unlock();
+        fileLock.unlock();
 
-        // update until stopped
-        if ( !analyzeDir( dir.first, dir.second ) ) {
-            break;
-        }
-        m_currentFolder.clear();
+        analyzeDir( dir.first, dir.second );
+        callDoIndexing();
     }
 
-    setIndexingStarted( false );
+    else {
+        // reset status
+        m_currentMutex.lock();
+        m_currentUrl.clear();
+        m_currentMutex.unlock();
 
-    // reset state
-    m_suspended = false;
-    m_stopped = false;
-    m_currentFolder.clear();
+        setIndexingStarted( false );
+    }
 }
 
+void Nepomuk::IndexScheduler::slotIndexingDone(KJob* job)
+{
+    kDebug() << job;
+    Q_UNUSED( job );
+
+    m_currentMutex.lock();
+    m_currentUrl.clear();
+    m_currentMutex.unlock();
 
-bool Nepomuk::IndexScheduler::analyzeDir( const QString& dir_, UpdateDirFlags flags )
+    callDoIndexing();
+}
+
+void Nepomuk::IndexScheduler::analyzeDir( const QString& dir_, Nepomuk::IndexScheduler::UpdateDirFlags flags )
 {
 //    kDebug() << dir << analyzer << recursive;
 
@@ -362,18 +363,20 @@ bool Nepomuk::IndexScheduler::analyzeDir( const QString& dir_, UpdateDirFlags fl
 
     // inform interested clients
     emit indexingFolder( dir );
+    m_currentMutex.lock();
+    m_currentUrl = KUrl( dir );
+    m_currentMutex.unlock();
 
-    m_currentFolder = dir;
     const bool recursive = flags&UpdateRecursive;
     const bool forceUpdate = flags&ForceUpdate;
 
     // we start by updating the folder itself
     QFileInfo dirInfo( dir );
     KUrl dirUrl( dir );
-    Nepomuk::Resource dirRes( dirUrl );
-    if ( !dirRes.exists() ||
-         dirRes.property( Nepomuk::Vocabulary::NIE::lastModified() ).toDateTime() != dirInfo.lastModified() ) {
-        m_indexer->indexFile( dirInfo );
+    if ( !compareIndexedMTime(dirUrl, dirInfo.lastModified()) ) {
+        KJob * indexer = new Indexer( dirInfo );
+        connect( indexer, SIGNAL(finished(KJob*)), this, SLOT(slotIndexingDone(KJob*)) );
+        indexer->start();
     }
 
     // get a map of all indexed files from the dir including their stored mtime
@@ -440,49 +443,43 @@ bool Nepomuk::IndexScheduler::analyzeDir( const QString& dir_, UpdateDirFlags fl
     deleteEntries( filesToDelete );
 
     // analyze all files that are new or need updating
-    foreach( const QFileInfo& file, filesToIndex ) {
-
-        // wait if we are suspended or return if we are stopped
-        if ( !waitForContinue() )
-            return false;
+    m_filesToUpdateMutex.lock();
+    m_filesToUpdate.append( filesToIndex );
+    m_filesToUpdateMutex.unlock();
 
-        m_currentUrl = file.filePath();
-        m_indexer->indexFile( file );
-        m_currentUrl = KUrl();
-    }
-
-    return true;
+    // reset status
+    m_currentMutex.lock();
+    m_currentUrl.clear();
+    m_currentMutex.unlock();
 }
 
 
-bool Nepomuk::IndexScheduler::waitForContinue( bool disableDelay )
+void Nepomuk::IndexScheduler::callDoIndexing(bool noDelay)
 {
-    QMutexLocker locker( &m_resumeStopMutex );
-    if ( m_suspended ) {
-        setIndexingStarted( false );
-        m_resumeStopWc.wait( &m_resumeStopMutex );
-        setIndexingStarted( true );
-    }
-    else if ( !disableDelay && m_speed != FullSpeed ) {
-        msleep( m_speed == ReducedSpeed ? s_reducedSpeedDelay : s_snailPaceDelay );
+    if( !m_suspended ) {
+        QTimer::singleShot( noDelay ? 0 : m_indexingDelay, this, SLOT(doIndexing()) );
     }
-
-    return !m_stopped;
 }
 
 
 void Nepomuk::IndexScheduler::updateDir( const QString& path, UpdateDirFlags flags )
 {
-    QMutexLocker lock( &m_dirsToUpdateMutex );
+    QMutexLocker dirLock( &m_dirsToUpdateMutex );
     m_dirsToUpdate.prependDir( path, flags & ~AutoUpdateFolder );
-    m_dirsToUpdateWc.wakeAll();
+
+    QMutexLocker statusLock( &m_indexingMutex );
+    if( !m_indexing )
+        callDoIndexing();
 }
 
 
 void Nepomuk::IndexScheduler::updateAll( bool forceUpdate )
 {
     queueAllFoldersForUpdate( forceUpdate );
-    m_dirsToUpdateWc.wakeAll();
+
+    QMutexLocker locker( &m_indexingMutex );
+    if( !m_indexing )
+        callDoIndexing();
 }
 
 
@@ -506,23 +503,45 @@ void Nepomuk::IndexScheduler::queueAllFoldersForUpdate( bool forceUpdate )
 
 void Nepomuk::IndexScheduler::slotConfigChanged()
 {
-    // restart to make sure we update all folders and removeOldAndUnwantedEntries
-    if ( isRunning() )
-        restart();
-}
+    // TODO: only update folders that were added in the config
+    updateAll();
 
+    if( m_cleaner ) {
+        m_cleaner->kill();
+        delete m_cleaner;
+    }
 
-void Nepomuk::IndexScheduler::analyzeResource( const QUrl& uri, const QDateTime& modificationTime, QDataStream& data )
-{
-    Indexer indexer;
-    indexer.indexResource( uri, modificationTime, data );
+    // TODO: only clean the folders that were removed from the config
+    m_cleaner = new IndexCleaner( this );
+    connect( m_cleaner, SIGNAL(finished(KJob*)), this, SLOT(slotCleaningDone()) );
+    m_cleaner->start();
 }
 
 
 void Nepomuk::IndexScheduler::analyzeFile( const QString& path )
 {
-    Indexer indexer;
-    indexer.indexFile( QFileInfo( path ) );
+    kDebug() << path;
+    QMutexLocker fileLock(&m_filesToUpdateMutex);
+
+    // we prepend the file to give preference to newly created and changed files over
+    // the initial indexing. Sadly operator== cannot be relied on for QFileInfo. Thus
+    // we need to do a dumb search
+    QMutableListIterator<QFileInfo> it(m_filesToUpdate);
+    while(it.hasNext()) {
+        if(it.next().filePath() == path) {
+            kDebug() << "Already queued:" << path << "Moving to front of queue.";
+            it.remove();
+            break;
+        }
+    }
+    kDebug() << "Queuing" << path;
+    m_filesToUpdate.prepend(path);
+
+    // continue indexing without any delay. We want changes reflected as soon as possible
+    QMutexLocker statusLock(&m_indexingMutex);
+    if( !m_indexing ) {
+        callDoIndexing(true);
+    }
 }
 
 
@@ -530,207 +549,17 @@ void Nepomuk::IndexScheduler::deleteEntries( const QStringList& entries )
 {
     // recurse into subdirs
     // TODO: use a less mem intensive method
+    // FIXME: There are 2 lists, one for KUrl and the other for QUrl. They should be automatically
+    //        converted. Fix this in 4.8
+    QList<QUrl> urls;
+    KUrl::List kurls;
     for ( int i = 0; i < entries.count(); ++i ) {
+        KUrl url( entries[i] );
+        urls << url;
+        kurls << url;
         deleteEntries( getChildren( entries[i] ).keys() );
-        m_indexer->clearIndexedData( KUrl( entries[i] ) );
-    }
-}
-
-
-namespace {
-    /**
-     * Creates one SPARQL filter expression that excludes the include folders.
-     * This is necessary since constructFolderSubFilter will append a slash to
-     * each folder to make sure it does not match something like
-     * '/home/foobar' with '/home/foo'.
-     */
-    QString constructExcludeIncludeFoldersFilter()
-    {
-        QStringList filters;
-        foreach( const QString& folder, Nepomuk::StrigiServiceConfig::self()->includeFolders() ) {
-            filters << QString::fromLatin1( "(?url!=%1)" ).arg( Soprano::Node::resourceToN3( KUrl( folder ) ) );
-        }
-        return filters.join( QLatin1String( " && " ) );
-    }
-
-    QString constructFolderSubFilter( const QList<QPair<QString, bool> > folders, int& index )
-    {
-        QString path = folders[index].first;
-        if ( !path.endsWith( '/' ) )
-            path += '/';
-        const bool include = folders[index].second;
-
-        ++index;
-
-        QStringList subFilters;
-        while ( index < folders.count() &&
-                folders[index].first.startsWith( path ) ) {
-            subFilters << constructFolderSubFilter( folders, index );
-        }
-
-        QString thisFilter = QString::fromLatin1( "REGEX(STR(?url),'^%1')" ).arg( QString::fromAscii( KUrl( path ).toEncoded() ) );
-
-        // we want all folders that should NOT be indexed
-        if ( include ) {
-            thisFilter.prepend( '!' );
-        }
-        subFilters.prepend( thisFilter );
-
-        if ( subFilters.count() > 1 ) {
-            return '(' + subFilters.join( include ? QLatin1String( " || " ) : QLatin1String( " && " ) ) + ')';
-        }
-        else {
-            return subFilters.first();
-        }
     }
-
-    /**
-     * Creates one SPARQL filter which matches all files and folders that should NOT be indexed.
-     */
-    QString constructFolderFilter()
-    {
-        QStringList subFilters( constructExcludeIncludeFoldersFilter() );
-
-        // now add the actual filters
-        QList<QPair<QString, bool> > folders = Nepomuk::StrigiServiceConfig::self()->folders();
-        int index = 0;
-        while ( index < folders.count() ) {
-            subFilters << constructFolderSubFilter( folders, index );
-        }
-        return subFilters.join(" && ");
-    }
-}
-
-void Nepomuk::IndexScheduler::removeOldAndUnwantedEntries()
-{
-    //
-    // We now query all indexed files that are in folders that should not
-    // be indexed at once.
-    //
-    QString folderFilter = constructFolderFilter();
-    if( !folderFilter.isEmpty() )
-        folderFilter = QString::fromLatin1("FILTER(%1) .").arg(folderFilter);
-
-    //
-    // We query all files that should not be in the store
-    // This for example excludes all filex:/ URLs.
-    //
-    QString query = QString::fromLatin1( "select distinct ?g where { "
-                                         "?r %1 ?url . "
-                                         "?g <http://www.strigi.org/fields#indexGraphFor> ?r . "
-                                         "FILTER(REGEX(STR(?url),'^file:/')) . "
-                                         "%2 }" )
-                    .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                          folderFilter );
-    kDebug() << query;
-    if ( !removeAllGraphsFromQuery( query ) )
-        return;
-
-
-    //
-    // Build filter query for all exclude filters
-    //
-    QStringList fileFilters;
-    foreach( const QString& filter, Nepomuk::StrigiServiceConfig::self()->excludeFilters() ) {
-        QString filterRxStr = QRegExp::escape( filter );
-        filterRxStr.replace( "\\*", QLatin1String( ".*" ) );
-        filterRxStr.replace( "\\?", QLatin1String( "." ) );
-        filterRxStr.replace( '\\',"\\\\" );
-        fileFilters << QString::fromLatin1( "REGEX(STR(?fn),\"^%1$\")" ).arg( filterRxStr );
-    }
-    QString includeExcludeFilters = constructExcludeIncludeFoldersFilter();
-
-    QString filters;
-    if( !includeExcludeFilters.isEmpty() && !fileFilters.isEmpty() )
-        filters = QString::fromLatin1("FILTER((%1) && (%2)) .").arg( includeExcludeFilters, fileFilters.join(" || ") );
-    else if( !fileFilters.isEmpty() )
-        filters = QString::fromLatin1("FILTER(%1) .").arg( fileFilters.join(" || ") );
-    else if( !includeExcludeFilters.isEmpty() )
-        filters = QString::fromLatin1("FILTER(%1) .").arg( includeExcludeFilters );
-
-    query = QString::fromLatin1( "select distinct ?g where { "
-                                 "?r %1 ?url . "
-                                 "?r %2 ?fn . "
-                                 "?g <http://www.strigi.org/fields#indexGraphFor> ?r . "
-                                 "FILTER(REGEX(STR(?url),\"^file:/\")) . "
-                                 "%3 }" )
-            .arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                  Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NFO::fileName() ),
-                  filters );
-    kDebug() << query;
-    if ( !removeAllGraphsFromQuery( query ) )
-        return;
-
-
-    //
-    // Remove all old data from Xesam-times. While we leave out the data created by libnepomuk
-    // there is no problem since libnepomuk still uses backwards compatible queries and we use
-    // libnepomuk to determine URIs in the strigi backend.
-    //
-    query = QString::fromLatin1( "select distinct ?g where { "
-                                 "?g <http://www.strigi.org/fields#indexGraphFor> ?x . "
-                                 "{ graph ?g { ?r1 <http://strigi.sf.net/ontologies/0.9#parentUrl> ?p1 . } } "
-                                 "UNION "
-                                 "{ graph ?g { ?r2 %1 ?u2 . } } "
-                                 "}" )
-            .arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::Xesam::url() ) );
-    kDebug() << query;
-    if ( !removeAllGraphsFromQuery( query ) )
-        return;
-
-
-    //
-    // Remove data which is useless but still around from before. This could happen due to some buggy version of
-    // the indexer or the filewatch service or even some application messing up the data.
-    // We look for indexed files that do not have a nie:url defined and thus, will never be catched by any of the
-    // other queries. In addition we check for an isPartOf relation since strigi produces EmbeddedFileDataObjects
-    // for video and audio streams.
-    //
-    Query::Query q(
-        Strigi::Ontology::indexGraphFor() == ( Soprano::Vocabulary::RDF::type() == Query::ResourceTerm( Nepomuk::Vocabulary::NFO::FileDataObject() ) &&
-                                               !( Nepomuk::Vocabulary::NIE::url() == Query::Term() ) &&
-                                               !( Nepomuk::Vocabulary::NIE::isPartOf() == Query::Term() ) )
-        );
-    q.setQueryFlags(Query::Query::NoResultRestrictions);
-    query = q.toSparqlQuery();
-    kDebug() << query;
-    removeAllGraphsFromQuery( query );
-}
-
-
-
-/**
- * Runs the query using a limit until all graphs have been deleted. This is not done
- * in one big loop to avoid the problems with messed up iterators when one of the iterated
- * item is deleted.
- */
-bool Nepomuk::IndexScheduler::removeAllGraphsFromQuery( const QString& query )
-{
-    while ( 1 ) {
-        // get the next batch of graphs
-        QList<Soprano::Node> graphs
-            = ResourceManager::instance()->mainModel()->executeQuery( query + QLatin1String( " LIMIT 200" ),
-                                                                      Soprano::Query::QueryLanguageSparql ).iterateBindings( 0 ).allNodes();
-
-        // remove all graphs in the batch
-        Q_FOREACH( const Soprano::Node& graph, graphs ) {
-
-            // wait for resume or stop (or simply continue)
-            if ( !waitForContinue() ) {
-                return false;
-            }
-
-            ResourceManager::instance()->mainModel()->removeContext( graph );
-        }
-
-        // we are done when the last graphs are queried
-        if ( graphs.count() < 200 ) {
-            return true;
-        }
-    }
-
-    // make gcc shut up
-    return true;
+    Nepomuk::clearIndexedData(urls);
 }
 
 
diff --git a/nepomuk/services/strigi/indexscheduler.h b/nepomuk/services/strigi/indexscheduler.h
index 6a03aa4..01d325e 100644
--- a/nepomuk/services/strigi/indexscheduler.h
+++ b/nepomuk/services/strigi/indexscheduler.h
@@ -24,19 +24,20 @@
 #include <QtCore/QWaitCondition>
 #include <QtCore/QQueue>
 #include <QtCore/QDateTime>
+#include <QtCore/QTimer>
 
 #include <vector>
 #include <string>
 
 #include <KUrl>
 
+class KJob;
 class QFileInfo;
 class QByteArray;
 
 namespace Nepomuk {
 
-    class Indexer;
-
+    class IndexCleaner;
     /**
      * The IndexScheduler performs the normal indexing,
      * ie. the initial indexing and the timed updates
@@ -44,12 +45,12 @@ namespace Nepomuk {
      *
      * Events are not handled.
      */
-    class IndexScheduler : public QThread
+    class IndexScheduler : public QObject
     {
         Q_OBJECT
 
     public:
-        IndexScheduler( QObject* parent );
+        IndexScheduler( QObject* parent=0 );
         ~IndexScheduler();
 
         bool isSuspended() const;
@@ -120,18 +121,14 @@ namespace Nepomuk {
             SnailPace
         };
 
-        IndexingSpeed currentSpeed() const { return m_speed; }
-
     public Q_SLOTS:
         void suspend();
         void resume();
-        void stop();
-        void restart();
 
         void setIndexingSpeed( IndexingSpeed speed );
 
         /**
-         * A convinience slot which calls setIndexingSpeed
+         * A convenience slot which calls setIndexingSpeed
          * with either FullSpeed or ReducedSpeed, based on the
          * value of \p reduced.
          */
@@ -162,38 +159,30 @@ namespace Nepomuk {
          */
         void analyzeFile( const QString& path );
 
-        /**
-         * Analyze a resource that is not read from the local harddisk.
-         *
-         * \param uri The resource URI to identify the resource.
-         * \param modificationTime The modification date of the resource. Used to determine if
-         *        an actual update is necessary.
-         * \data The data to analyze, ie. the contents of the resource.
-         */
-        void analyzeResource( const QUrl& uri, const QDateTime& modificationTime, QDataStream& data );
-
     Q_SIGNALS:
         void indexingStarted();
         void indexingStopped();
+
         /// a combination of the two signals above
         void indexingStateChanged( bool indexing );
         void indexingFolder( const QString& );
+        void indexingFile( const QString & );
         void indexingSuspended( bool suspended );
 
     private Q_SLOTS:
         void slotConfigChanged();
+        void slotCleaningDone();
+        void slotIndexingDone( KJob* job );
+        void doIndexing();
 
     private:
-        void run();
-
-        bool waitForContinue( bool disableDelay = false );
-
         /**
-         * It first indexes \p dir. Then it indexes all the files in \p dir, and
-         * finally recursivly analyzes all the subfolders in \p dir IF \p flags
-         * contains the 'UpdateRecursive' flag. It even sets m_currentFolder
+         * It first indexes \p dir. Then it checks all the files in \p dir
+         * against the configuration and the data in Nepomuk to fill
+         * m_filesToUpdate. No actual indexing is done besides \p dir
+         * itself.
          */
-        bool analyzeDir( const QString& dir, UpdateDirFlags flags );
+        void analyzeDir( const QString& dir, UpdateDirFlags flags );
 
         void queueAllFoldersForUpdate( bool forceUpdate = false );
 
@@ -208,20 +197,16 @@ namespace Nepomuk {
         void setIndexingStarted( bool started );
 
         /**
-         * Removes all previously indexed entries that are not in the list of folders
-         * to index anymore.
+         * Continue indexing async after waiting for the configured delay.
+         * \param noDelay If true indexing will be started immediately without any delay.
          */
-        void removeOldAndUnwantedEntries();
-        bool removeAllGraphsFromQuery( const QString& query_ );
+        void callDoIndexing( bool noDelay = false );
 
         bool m_suspended;
-        bool m_stopped;
         bool m_indexing;
 
-        QMutex m_resumeStopMutex;
-        QWaitCondition m_resumeStopWc;
-
-        Indexer* m_indexer;
+        mutable QMutex m_suspendMutex;
+        mutable QMutex m_indexingMutex;
 
         // A specialized queue that gives priority to dirs that do not use the AutoUpdateFolder flag.
         class UpdateDirQueue : public QQueue<QPair<QString, UpdateDirFlags> >
@@ -234,13 +219,17 @@ namespace Nepomuk {
         // queue of folders to update (+flags defined in the source file) - changed by updateDir
         UpdateDirQueue m_dirsToUpdate;
 
+        // queue of files to update. This is filled from the dirs queue and manual methods like analyzeFile
+        QQueue<QFileInfo> m_filesToUpdate;
+
         QMutex m_dirsToUpdateMutex;
-        QWaitCondition m_dirsToUpdateWc;
+        QMutex m_filesToUpdateMutex;
 
-        QString m_currentFolder;
+        mutable QMutex m_currentMutex;
         KUrl m_currentUrl;
 
-        IndexingSpeed m_speed;
+        int m_indexingDelay;
+        IndexCleaner* m_cleaner;
     };
 
     QDebug operator<<( QDebug dbg, IndexScheduler::IndexingSpeed speed );
@@ -249,3 +238,4 @@ namespace Nepomuk {
 Q_DECLARE_OPERATORS_FOR_FLAGS(Nepomuk::IndexScheduler::UpdateDirFlags)
 
 #endif
+
diff --git a/nepomuk/services/strigi/nepomukindexer.cpp b/nepomuk/services/strigi/nepomukindexer.cpp
index 0681a5e..d796983 100644
--- a/nepomuk/services/strigi/nepomukindexer.cpp
+++ b/nepomuk/services/strigi/nepomukindexer.cpp
@@ -1,6 +1,6 @@
 /*
    This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2010-2011 Sebastian Trueg <trueg@kde.org>
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
@@ -20,211 +20,51 @@
 */
 
 #include "nepomukindexer.h"
-#include "nepomukindexwriter.h"
-#include "nepomukindexfeeder.h"
+#include "util.h"
 
-#include <Nepomuk/ResourceManager>
 #include <Nepomuk/Resource>
-#include <Nepomuk/Variant>
-#include <Nepomuk/Vocabulary/NIE>
 
 #include <KUrl>
 #include <KDebug>
+#include <KProcess>
+#include <KStandardDirs>
 
-#include <QtCore/QDataStream>
-#include <QtCore/QDateTime>
-#include <QtCore/QFile>
 #include <QtCore/QFileInfo>
 
-#include <strigi/strigiconfig.h>
-#include <strigi/indexwriter.h>
-#include <strigi/analysisresult.h>
-#include <strigi/fileinputstream.h>
-#include <strigi/analyzerconfiguration.h>
-
-
-namespace {
-    class StoppableConfiguration : public Strigi::AnalyzerConfiguration
-    {
-    public:
-        StoppableConfiguration()
-            : m_stop(false) {
-#if defined(STRIGI_IS_VERSION)
-#if STRIGI_IS_VERSION( 0, 6, 1 )
-            setIndexArchiveContents( false );
-#endif
-#endif
-        }
-
-        bool indexMore() const {
-            return !m_stop;
-        }
-
-        bool addMoreText() const {
-            return !m_stop;
-        }
-
-        void setStop( bool s ) {
-            m_stop = s;
-        }
-
-    private:
-        bool m_stop;
-    };
-}
-
-
-class Nepomuk::Indexer::Private
-{
-public:
-    StoppableConfiguration m_analyzerConfig;
-    IndexFeeder* m_indexFeeder;
-    StrigiIndexWriter* m_indexWriter;
-    Strigi::StreamAnalyzer* m_streamAnalyzer;
-};
-
 
-Nepomuk::Indexer::Indexer( QObject* parent )
-    : QObject( parent ),
-      d( new Private() )
+Nepomuk::Indexer::Indexer(const KUrl& localUrl, QObject* parent)
+    : KJob(parent),
+      m_url( localUrl ),
+      m_exitCode( -1 )
 {
-    d->m_indexFeeder = new IndexFeeder( this );
-    d->m_indexWriter = new StrigiIndexWriter( d->m_indexFeeder );
-    d->m_streamAnalyzer = new Strigi::StreamAnalyzer( d->m_analyzerConfig );
-    d->m_streamAnalyzer->setIndexWriter( *d->m_indexWriter );
 }
 
-
-Nepomuk::Indexer::~Indexer()
+Nepomuk::Indexer::Indexer(const QFileInfo& info, QObject* parent)
+    : KJob(parent),
+      m_url( info.absoluteFilePath() ),
+      m_exitCode( -1 )
 {
-    delete d->m_streamAnalyzer;
-    delete d->m_indexWriter;
-    delete d->m_indexFeeder;
-    delete d;
-}
-
-
-void Nepomuk::Indexer::indexFile( const KUrl& url )
-{
-    indexFile( QFileInfo( url.toLocalFile() ) );
-}
-
-
-void Nepomuk::Indexer::indexFile( const QFileInfo& info )
-{
-    d->m_analyzerConfig.setStop( false );
-
-    KUrl url( info.filePath() );
-
-    // strigi asserts if the file path has a trailing slash
-    QString filePath = url.toLocalFile( KUrl::RemoveTrailingSlash );
-    QString dir = url.directory( KUrl::IgnoreTrailingSlash );
-
-    Strigi::AnalysisResult analysisresult( QFile::encodeName( filePath ).data(),
-                                           info.lastModified().toTime_t(),
-                                           *d->m_indexWriter,
-                                           *d->m_streamAnalyzer,
-                                           QFile::encodeName( dir ).data() );
-    if ( info.isFile() && !info.isSymLink() ) {
-#ifdef STRIGI_HAS_FILEINPUTSTREAM_OPEN
-        Strigi::InputStream* stream = Strigi::FileInputStream::open( QFile::encodeName( info.filePath() ) );
-        analysisresult.index( stream );
-        delete stream;
-#else
-        Strigi::FileInputStream stream( QFile::encodeName( info.filePath() ) );
-        analysisresult.index( &stream );
-#endif
-    }
-    else {
-        analysisresult.index(0);
-    }
-}
-
-
-namespace {
-    class QDataStreamStrigiBufferedStream : public Strigi::BufferedStream<char>
-    {
-    public:
-        QDataStreamStrigiBufferedStream( QDataStream& stream )
-            : m_stream( stream ) {
-        }
-
-        int32_t fillBuffer( char* start, int32_t space ) {
-            int r = m_stream.readRawData( start, space );
-            if ( r == 0 ) {
-                // Strigi's API is so weird!
-                return -1;
-            }
-            else if ( r < 0 ) {
-                // Again: weird API. m_status is a protected member of StreamBaseBase (yes, 2x Base)
-                m_status = Strigi::Error;
-                return -1;
-            }
-            else {
-                return r;
-            }
-        }
-
-    private:
-        QDataStream& m_stream;
-    };
 }
 
-
-void Nepomuk::Indexer::indexResource( const KUrl& uri, const QDateTime& modificationTime, QDataStream& data )
-{
-    d->m_analyzerConfig.setStop( false );
-
-    Resource dirRes( uri );
-    if ( !dirRes.exists() ||
-         dirRes.property( Nepomuk::Vocabulary::NIE::lastModified() ).toDateTime() != modificationTime ) {
-        Strigi::AnalysisResult analysisresult( uri.toEncoded().data(),
-                                               modificationTime.toTime_t(),
-                                               *d->m_indexWriter,
-                                               *d->m_streamAnalyzer );
-        QDataStreamStrigiBufferedStream stream( data );
-        analysisresult.index( &stream );
-    }
-    else {
-        kDebug() << uri << "up to date";
-    }
-}
-
-
-void Nepomuk::Indexer::clearIndexedData( const KUrl& url )
-{
-    Nepomuk::IndexFeeder::clearIndexedDataForUrl( url );
-}
-
-
-void Nepomuk::Indexer::clearIndexedData( const QFileInfo& info )
+void Nepomuk::Indexer::start()
 {
-    Nepomuk::IndexFeeder::clearIndexedDataForUrl( KUrl(info.filePath()) );
-}
+    const QString exe = KStandardDirs::findExe(QLatin1String("nepomukindexer"));
+    m_process = new KProcess( this );
+    m_process->setProgram( exe, QStringList() << m_url.toLocalFile() );
 
+    kDebug() << "Running" << exe << m_url.toLocalFile();
 
-void Nepomuk::Indexer::clearIndexedData( const Nepomuk::Resource& res )
-{
-    Nepomuk::IndexFeeder::clearIndexedDataForResourceUri( res.resourceUri() );
+    connect( m_process, SIGNAL(finished(int)), this, SLOT(slotIndexedFile(int)) );
+    m_process->start();
 }
 
 
-void Nepomuk::Indexer::stop()
+void Nepomuk::Indexer::slotIndexedFile(int exitCode)
 {
-    kDebug();
-    d->m_analyzerConfig.setStop( true );
-    d->m_indexFeeder->stop();
-    d->m_indexFeeder->wait();
-    kDebug() << "done";
-}
-
+    kDebug() << "Indexing of " << m_url.toLocalFile() << "finished with exit code" << exitCode;
+    m_exitCode = exitCode;
 
-void Nepomuk::Indexer::start()
-{
-    kDebug();
-    d->m_analyzerConfig.setStop( false );
-    d->m_indexFeeder->start();
-    kDebug() << "Started";
+    emitResult();
 }
 
 #include "nepomukindexer.moc"
diff --git a/nepomuk/services/strigi/nepomukindexer.h b/nepomuk/services/strigi/nepomukindexer.h
index 58fbd8b..08abb2a 100644
--- a/nepomuk/services/strigi/nepomukindexer.h
+++ b/nepomuk/services/strigi/nepomukindexer.h
@@ -22,11 +22,10 @@
 #ifndef _NEPOMUK_INDEXER_H_
 #define _NEPOMUK_INDEXER_H_
 
-#include <QtCore/QObject>
+#include <KJob>
+#include <KUrl>
 
-class KUrl;
-class QDateTime;
-class QDataStream;
+class KProcess;
 class QFileInfo;
 
 namespace Nepomuk {
@@ -48,71 +47,16 @@ namespace Nepomuk {
      *
      * \author Sebastian Trueg <trueg@kde.org>
      */
-    class Indexer : public QObject
+    class Indexer : public KJob
     {
         Q_OBJECT
 
     public:
-        /**
-         * Create a new indexer.
-         */
-        Indexer( QObject* parent = 0 );
-
-        /**
-         * Destructor
-         */
-        ~Indexer();
+        Indexer( const QFileInfo& info, QObject* parent = 0 );
+        Indexer( const KUrl& localUrl, QObject* parent = 0 );
 
-        /**
-         * Index a single local file or folder (files in a folder will
-         * not be indexed recursively).
-         */
-        void indexFile( const KUrl& localUrl );
+        virtual void start();
 
-        /**
-         * Index a single local file or folder (files in a folder will
-         * not be indexed recursively). This method does the exact same
-         * as the above except that it saves an addditional stat of the
-         * file.
-         */
-        void indexFile( const QFileInfo& info );
-
-        /**
-         * Index a random resource.
-         *
-         * \param url The URL of the resource.
-         * \param modificationTime the last modification date of the resource.
-         * \param data The data to be analyzed
-         */
-        void indexResource( const KUrl& url, const QDateTime& modificationTime, QDataStream& data );
-
-        /**
-         * Remove all indexed data for the file/resource identified by \p res.
-         */
-        void clearIndexedData( const Nepomuk::Resource& res );
-
-        /**
-         * \overload
-         *
-         * Remove all indexed data for the file at local url \p url.
-         */
-        void clearIndexedData( const KUrl& url );
-
-        /**
-         * \overload
-         *
-         * Remove all indexed data for the file identifies by \p info.
-         */
-        void clearIndexedData( const QFileInfo& info );
-
-        /**
-         * This method stops the indexer, i.e. cancels potentially
-         * long indexing operations and any queued data is thrown away.
-         */
-        void stop();
-
-        void start();
-        
     Q_SIGNALS:
         /**
          * Emitted once the indexing for a file or resource has finished.
@@ -126,9 +70,13 @@ namespace Nepomuk {
         // TODO: better only have one method for success and failure.
         // TODO: actually emit the indexingDone signal
 
+    private slots:
+        void slotIndexedFile(int exitCode);
+
     private:
-        class Private;
-        Private* const d;
+        KUrl m_url;
+        KProcess* m_process;
+        int m_exitCode;
     };
 }
 
diff --git a/nepomuk/services/strigi/nepomukindexfeeder.cpp b/nepomuk/services/strigi/nepomukindexfeeder.cpp
deleted file mode 100644
index a5fb626..0000000
--- a/nepomuk/services/strigi/nepomukindexfeeder.cpp
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010 Vishesh Handa <handa.vish@gmail.com>
-   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-#include "nepomukindexfeeder.h"
-#include "util.h"
-
-#include <QtCore/QDateTime>
-
-#include <Soprano/Model>
-#include <Soprano/Statement>
-#include <Soprano/QueryResultIterator>
-#include <Soprano/Vocabulary/RDF>
-#include <Soprano/Vocabulary/NAO>
-#include <Soprano/Vocabulary/NRL>
-
-#include <Nepomuk/Vocabulary/NIE>
-#include <Nepomuk/ResourceManager>
-#include <Nepomuk/Resource>
-
-#include <KDebug>
-
-
-Nepomuk::IndexFeeder::IndexFeeder( QObject* parent )
-    : QThread( parent )
-{
-    m_stopped = false;
-    start();
-}
-
-
-Nepomuk::IndexFeeder::~IndexFeeder()
-{
-    stop();
-    wait();
-}
-
-
-void Nepomuk::IndexFeeder::begin( const QUrl & url )
-{
-    //kDebug() << "BEGINING";
-    Request req;
-    req.uri = url;
-
-    m_stack.push( req );
-}
-
-
-void Nepomuk::IndexFeeder::addStatement(const Soprano::Statement& st)
-{
-    Q_ASSERT( !m_stack.isEmpty() );
-    Request & req = m_stack.top();
-
-    const Soprano::Node & n = st.subject();
-
-    QUrl uriOrId;
-    if( n.isResource() )
-        uriOrId = n.uri();
-    else if( n.isBlank() )
-        uriOrId = n.identifier();
-
-    if( !req.hash.contains( uriOrId ) ) {
-        ResourceStruct rs;
-        if( n.isResource() )
-            rs.uri = n.uri();
-
-        req.hash.insert( uriOrId, rs );
-    }
-
-    ResourceStruct & rs = req.hash[ uriOrId ];
-    rs.propHash.insert( st.predicate().uri(), st.object() );
-}
-
-
-void Nepomuk::IndexFeeder::addStatement(const Soprano::Node& subject, const Soprano::Node& predicate, const Soprano::Node& object)
-{
-    addStatement( Soprano::Statement( subject, predicate, object, Soprano::Node() ) );
-}
-
-
-void Nepomuk::IndexFeeder::end( bool forceCommit )
-{
-    if( m_stack.isEmpty() )
-        return;
-    //kDebug() << "ENDING";
-
-    Request req = m_stack.pop();
-
-    if ( forceCommit ) {
-        handleRequest( req );
-    }
-    else {
-        QMutexLocker lock( &m_queueMutex );
-        m_updateQueue.enqueue( req );
-        m_queueWaiter.wakeAll();
-    }
-}
-
-
-void Nepomuk::IndexFeeder::stop()
-{
-    QMutexLocker lock( &m_queueMutex );
-    m_stopped = true;
-    m_queueWaiter.wakeAll();
-}
-
-
-QString Nepomuk::IndexFeeder::buildResourceQuery(const Nepomuk::IndexFeeder::ResourceStruct& rs) const
-{
-    QString query = QString::fromLatin1("select distinct ?r where { ");
-
-    QList<QUrl> keys = rs.propHash.uniqueKeys();
-    foreach( const QUrl & prop, keys ) {
-        const QList<Soprano::Node>& values = rs.propHash.values( prop );
-
-        foreach( const Soprano::Node & n, values ) {
-            query += " ?r " + Soprano::Node::resourceToN3( prop ) + " " + n.toN3() + " . ";
-        }
-    }
-    query += " } LIMIT 1";
-    return query;
-}
-
-
-void Nepomuk::IndexFeeder::addToModel(const Nepomuk::IndexFeeder::ResourceStruct& rs) const
-{
-    QUrl context = generateGraph( rs.uri );
-    QHashIterator<QUrl, Soprano::Node> iter( rs.propHash );
-    while( iter.hasNext() ) {
-        iter.next();
-
-        Soprano::Statement st( rs.uri, iter.key(), iter.value(), context );
-        //kDebug() << "ADDING : " << st;
-        ResourceManager::instance()->mainModel()->addStatement( st );
-    }
-}
-
-
-//BUG: When indexing a file, there is one main uri ( in Request ) and other additional uris
-//     If there is a statement connecting the main uri with the additional ones, it will be
-//     resolved correctly, but not if one of the additional one links to another additional one.
-void Nepomuk::IndexFeeder::run()
-{
-    m_stopped = false;
-    while( !m_stopped ) {
-
-        // lock for initial iteration
-        m_queueMutex.lock();
-
-        // work the queue
-        while( !m_updateQueue.isEmpty() ) {
-            Request request = m_updateQueue.dequeue();
-
-            // unlock after queue utilization
-            m_queueMutex.unlock();
-
-            handleRequest( request );
-
-            // lock for next iteration
-            m_queueMutex.lock();
-        }
-
-        // FIXME: this is not very secure. In theory m_stopped could be changed
-        // just after the if but before the m_queueWaiter has been entered.
-        // Then we would just hang there forever!
-        if ( !m_stopped ) {
-            // wait for more input
-            kDebug() << "Waiting...";
-            m_queueWaiter.wait( &m_queueMutex );
-            m_queueMutex.unlock();
-            kDebug() << "Woke up.";
-        }
-
-    }
-}
-
-
-void Nepomuk::IndexFeeder::handleRequest( Request& request ) const
-{
-    // Search for the resources or create them
-    //kDebug() << " Searching for duplicates or creating them ... ";
-    QMutableHashIterator<QUrl, ResourceStruct> it( request.hash );
-    while( it.hasNext() ) {
-        it.next();
-
-        // If it already exists
-        ResourceStruct & rs = it.value();
-        if( !rs.uri.isEmpty() )
-            continue;
-
-        QString query = buildResourceQuery( rs );
-        //kDebug() << query;
-        Soprano::QueryResultIterator it = ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
-
-        if( it.next() ) {
-            //kDebug() << "Found exact match " << rs.uri << " " << it[0].uri();
-            rs.uri = it[0].uri();
-        }
-        else {
-            //kDebug() << "Creating ..";
-            rs.uri = ResourceManager::instance()->generateUniqueUri( QString() );
-
-            // Add to the repository
-            addToModel( rs );
-        }
-    }
-
-    // Fix links for main
-    ResourceStruct & rs = request.hash[ request.uri ];
-    QMutableHashIterator<QUrl, Soprano::Node> iter( rs.propHash );
-    while( iter.hasNext() ) {
-        iter.next();
-        Soprano::Node & n = iter.value();
-
-        if( n.isEmpty() )
-            continue;
-
-        if( n.isBlank() ) {
-            const QString & id = n.identifier();
-            if( !request.hash.contains( id ) )
-                continue;
-            QUrl newUri = request.hash.value( id ).uri;
-            //kDebug() << id << " ---> " << newUri;
-            iter.value() = Soprano::Node( newUri );
-        }
-    }
-
-    // Add main file to the repository
-    addToModel( rs );
-}
-
-
-QUrl Nepomuk::IndexFeeder::generateGraph( const QUrl& resourceUri ) const
-{
-    Soprano::Model* model = ResourceManager::instance()->mainModel();
-    QUrl context = Nepomuk::ResourceManager::instance()->generateUniqueUri( "ctx" );
-
-    // create the provedance data for the data graph
-    // TODO: add more data at some point when it becomes of interest
-    QUrl metaDataContext = Nepomuk::ResourceManager::instance()->generateUniqueUri( "ctx" );
-    model->addStatement( context,
-                         Soprano::Vocabulary::RDF::type(),
-                         Soprano::Vocabulary::NRL::DiscardableInstanceBase(),
-                         metaDataContext );
-    model->addStatement( context,
-                         Soprano::Vocabulary::NAO::created(),
-                         Soprano::LiteralValue( QDateTime::currentDateTime() ),
-                         metaDataContext );
-    model->addStatement( context,
-                         Strigi::Ontology::indexGraphFor(),
-                         resourceUri,
-                         metaDataContext );
-    model->addStatement( metaDataContext,
-                         Soprano::Vocabulary::RDF::type(),
-                         Soprano::Vocabulary::NRL::GraphMetadata(),
-                         metaDataContext );
-    model->addStatement( metaDataContext,
-                         Soprano::Vocabulary::NRL::coreGraphMetadataFor(),
-                         context,
-                         metaDataContext );
-
-    return context;
-}
-
-
-// static
-bool Nepomuk::IndexFeeder::clearIndexedDataForUrl( const KUrl& url )
-{
-    if ( url.isEmpty() )
-        return false;
-
-    QString query = QString::fromLatin1( "select ?g where { "
-                                         "{ "
-                                         "?r %2 %1 . "
-                                         "?g %3 ?r . } "
-                                         "UNION "
-                                         "{ ?g %3 %1 . }"
-                                         "}")
-                    .arg( Soprano::Node::resourceToN3( url ),
-                          Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
-                          Soprano::Node::resourceToN3( Strigi::Ontology::indexGraphFor() ) );
-
-    Soprano::QueryResultIterator result = Nepomuk::ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
-    while ( result.next() ) {
-        // delete the indexed data (The Soprano::NRLModel in the storage service will take care of
-        // the metadata graph)
-        Nepomuk::ResourceManager::instance()->mainModel()->removeContext( result.binding( "g" ) );
-    }
-
-    return true;
-}
-
-
-// static
-bool Nepomuk::IndexFeeder::clearIndexedDataForResourceUri( const KUrl& res )
-{
-    if ( res.isEmpty() )
-        return false;
-
-    QString query = QString::fromLatin1( "select ?g where { ?g %1 %2 . }" )
-                    .arg( Soprano::Node::resourceToN3( Strigi::Ontology::indexGraphFor() ),
-                          Soprano::Node::resourceToN3( res ) );
-
-    Soprano::QueryResultIterator result = Nepomuk::ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
-    while ( result.next() ) {
-        // delete the indexed data (The Soprano::NRLModel in the storage service will take care of
-        // the metadata graph)
-        Nepomuk::ResourceManager::instance()->mainModel()->removeContext( result.binding( "g" ) );
-    }
-
-    return true;
-}
diff --git a/nepomuk/services/strigi/nepomukindexfeeder.h b/nepomuk/services/strigi/nepomukindexfeeder.h
deleted file mode 100644
index c4d4ed3..0000000
--- a/nepomuk/services/strigi/nepomukindexfeeder.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
-   This file is part of the Nepomuk KDE project.
-   Copyright (C) 2010 Vishesh Handa <handa.vish@gmail.com>
-   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) version 3, or any
-   later version accepted by the membership of KDE e.V. (or its
-   successor approved by the membership of KDE e.V.), which shall
-   act as a proxy defined in Section 6 of version 3 of the license.
-
-   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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-#ifndef NEPOMUKINDEXFEEDER_H
-#define NEPOMUKINDEXFEEDER_H
-
-#include <QtCore/QThread>
-#include <QtCore/QMutex>
-#include <QtCore/QWaitCondition>
-#include <QtCore/QUrl>
-#include <QtCore/QQueue>
-#include <QtCore/QStack>
-#include <QtCore/QSet>
-
-namespace Soprano {
-    class Statement;
-    class Node;
-}
-class KUrl;
-
-namespace Nepomuk {
-    class IndexFeeder : public QThread
-    {
-        Q_OBJECT
-    public:
-        IndexFeeder( QObject* parent = 0 );
-        virtual ~IndexFeeder();
-
-        void stop();
-
-    public Q_SLOTS:
-        /**
-         * Starts the feeding process for a file with resourceUri \p uri.
-         * This function should be called before adding any statements.
-         * The function may be called repeatedly.
-         *
-         * Should be preceeded by an end()
-         *
-         * \sa end
-         */
-        void begin( const QUrl & uri );
-
-        /**
-         * Adds \p st to the list of statements to be added.
-         * \p st may contain Blank Nodes.The context is ignored.
-         * Should be called between begin and end
-         *
-         * \sa begin end
-         */
-        void addStatement( const Soprano::Statement & st );
-
-        /**
-         * Adds the subject, predicate, object to the list of statements
-         * to be added. The Subject or Object may contain Blank Nodes
-         * Should be called between begin and end
-         *
-         * \sa begin end
-         */
-        void addStatement( const Soprano::Node & subject,
-                           const Soprano::Node & predicate,
-                           const Soprano::Node & object );
-
-        /**
-         * Finishes the feeding process, and starts with the resolution and merging
-         * of resources based on the statements provided.
-         *
-         * addStatement should not be called after this function, unless begin has already
-         * been called.
-         *
-         * \param forceCommit If true the request will be handled syncroneously. This is used
-         * to commit folder resources to Nepomuk right away since they might be needed later on.
-         *
-         * \sa begin
-         */
-        void end( bool forceCommit = false );
-
-        static bool clearIndexedDataForUrl( const KUrl& url );
-        static bool clearIndexedDataForResourceUri( const KUrl& res );
-
-    private:
-
-        struct ResourceStruct {
-            QUrl uri;
-            QMultiHash<QUrl, Soprano::Node> propHash;
-        };
-
-        // Maps the uri to the ResourceStuct
-        typedef QHash<QUrl, ResourceStruct> ResourceHash;
-
-        struct Request {
-            QUrl uri;
-            ResourceHash hash;
-        };
-
-        /// The thread uses this queue to check if it has any requests that need processing
-        QQueue<Request> m_updateQueue;
-
-        /**
-         * The stack is used to store the internal state of the Feeder, a new item is pushed into
-         * the stack everytime begin() is called, and the top most item is poped and sent into the
-         * update queue when end() is called.
-         */
-        QStack<Request> m_stack;
-
-        QMutex m_queueMutex;
-        QWaitCondition m_queueWaiter;
-        bool m_stopped;
-
-        void run();
-
-        /**
-         * Handle a single request, i.e. store all its data to Nepomuk.
-         */
-        void handleRequest( Request& request ) const;
-
-        /// Generates a discardable graph for \p resourceUri
-        QUrl generateGraph( const QUrl& resourceUri ) const;
-
-        /**
-         * Creates a sparql query which returns 1 resource which matches all the properties,
-         * and objects present in the propHash of the ResourceStruct
-         */
-        QString buildResourceQuery( const ResourceStruct & rs ) const;
-
-        /**
-         * Adds all the statements present in the ResourceStruct to the internal model.
-         * The contex is created via generateGraph
-         *
-         * \sa generateGraph
-         */
-        void addToModel( const ResourceStruct &rs ) const;
-    };
-
-}
-
-#endif // NEPOMUKINDEXFEEDER_H
diff --git a/nepomuk/services/strigi/nepomukindexwriter.cpp b/nepomuk/services/strigi/nepomukindexwriter.cpp
deleted file mode 100644
index afccb57..0000000
--- a/nepomuk/services/strigi/nepomukindexwriter.cpp
+++ /dev/null
@@ -1,665 +0,0 @@
-/*
-  Copyright (C) 2007-2010 Sebastian Trueg <trueg@kde.org>
-  Copyright (C) 2010 Vishesh Handa <handa.vish@gmail.com>
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU 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 library; see the file COPYING.  If not, write to
-  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-  Boston, MA 02110-1301, USA.
-*/
-
-#include "nepomukindexwriter.h"
-#include "nepomukindexfeeder.h"
-#include "util.h"
-
-#include <Soprano/Vocabulary/RDF>
-#include <Soprano/LiteralValue>
-#include <Soprano/Node>
-#include <Soprano/QueryResultIterator>
-
-#include <QtCore/QList>
-#include <QtCore/QHash>
-#include <QtCore/QVariant>
-#include <QtCore/QFileInfo>
-#include <QtCore/QFile>
-#include <QtCore/QUrl>
-#include <QtCore/QDateTime>
-#include <QtCore/QByteArray>
-#include <QtCore/QStack>
-
-#include <KUrl>
-#include <KDebug>
-
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include <map>
-#include <sstream>
-#include <algorithm>
-
-#include <Nepomuk/Types/Property>
-#include <Nepomuk/Types/Class>
-#include <Nepomuk/Types/Literal>
-#include <Nepomuk/ResourceManager>
-#include <Nepomuk/Resource>
-#include <Nepomuk/Vocabulary/NFO>
-#include <Nepomuk/Vocabulary/NIE>
-
-
-
-// IMPORTANT: strings in Strigi are apparently UTF8! Except for file names. Those are in local encoding.
-
-using namespace Soprano;
-using namespace Strigi;
-
-
-uint qHash( const std::string& s )
-{
-    return qHash( s.c_str() );
-}
-
-namespace {
-    QString findArchivePath( const QString& path ) {
-        QString p( path );
-        int i = 0;
-        while ( ( i = p.lastIndexOf( '/' ) ) > 0 ) {
-            p.truncate( i );
-            if ( QFileInfo( p ).isFile() ) {
-                return p;
-            }
-        }
-        return QString();
-    }
-
-    QUrl createFileUrl( const Strigi::AnalysisResult* idx ) {
-        // HACK: Strigi includes analysers that recurse into tar or zip archives and index
-        // the files therein. In KDE these files could perfectly be handled through kio slaves
-        // such as tar:/ or zip:/
-        // Here we try to use KDE-compatible URIs for these indexed files the best we can
-        // everything else defaults to file:/
-        QUrl uri;
-        QString path = QFile::decodeName( idx->path().c_str() );
-        if ( KUrl::isRelativeUrl( path ) )
-            uri = QUrl::fromLocalFile( QFileInfo( path ).absoluteFilePath() );
-        else
-            uri = KUrl( path ); // try to support http and other URLs
-
-        if ( idx->depth() > 0 ) {
-            QString archivePath = findArchivePath( path );
-            if ( QFile::exists( archivePath ) ) {
-                if ( archivePath.endsWith( QLatin1String( ".tar" ) ) ||
-                     archivePath.endsWith( QLatin1String( ".tar.gz" ) ) ||
-                     archivePath.endsWith( QLatin1String( ".tar.bz2" ) ) ||
-                     archivePath.endsWith( QLatin1String( ".tar.lzma" ) ) ||
-                     archivePath.endsWith( QLatin1String( ".tar.xz" ) ) ) {
-                    uri.setScheme( "tar" );
-                }
-                else if ( archivePath.endsWith( QLatin1String( ".zip" ) ) ) {
-                    uri.setScheme( "zip" );
-                }
-            }
-        }
-
-        // fallback for all
-        if ( uri.scheme().isEmpty() ) {
-            uri.setScheme( "file" );
-        }
-
-        return uri;
-    }
-
-
-    /**
-     * Creates a Blank or Resource Node based on the contents of the string provided.
-     * If the string is of the form ':identifier', a Blank node is created.
-     * Otherwise a Resource Node is returned.
-     */
-    Soprano::Node createBlankOrResourceNode( const std::string & str ) {
-        QString identifier = QString::fromUtf8( str.c_str() );
-
-        if( !identifier.isEmpty() && identifier[0] == ':' ) {
-            identifier.remove( 0, 1 );
-            return Soprano::Node::createBlankNode( identifier );
-        }
-
-        //Not a blank node
-        return Soprano::Node( QUrl(identifier) );
-    }
-
-
-    /**
-     * A simple cache for properties that are used in Strigi.
-     * This avoids matching them again and again.
-     *
-     * Also the class provides easy conversion methods for
-     * values provided by Strigi to values that Nepomuk understands.
-     */
-    class RegisteredFieldData
-    {
-    public:
-        RegisteredFieldData( const Nepomuk::Types::Property& prop, QVariant::Type t )
-            : m_property( prop ),
-              m_dataType( t ),
-              m_isRdfType( prop == Soprano::Vocabulary::RDF::type() ) {
-        }
-
-        const Nepomuk::Types::Property& property() const { return m_property; }
-        bool isRdfType() const { return m_isRdfType; }
-
-        Soprano::Node createObject( const std::string& value );
-
-    private:
-        Soprano::LiteralValue createLiteralValue( const std::string& value );
-
-        /// The actual property URI
-        Nepomuk::Types::Property m_property;
-
-        /// the literal range of the property (if applicable)
-        QVariant::Type m_dataType;
-
-        /// caching QUrl comparison
-        bool m_isRdfType;
-    };
-
-
-    /**
-     * Data objects that are used to store information relative to one
-     * indexing run.
-     */
-    class FileMetaData
-    {
-    public:
-        FileMetaData( const Strigi::AnalysisResult* idx );
-
-        /// stores basic data including the nie:url and the nrl:GraphMetadata in \p model
-        void storeBasicData( Nepomuk::IndexFeeder* feeder );
-
-        /// map a blank node to a resource
-        QUrl mapNode( const std::string& s );
-
-        /// The resource URI
-        QUrl resourceUri;
-
-        /// The file URL (nie:url)
-        KUrl fileUrl;
-
-        /// The file info - saved to prevent multiple stats
-        QFileInfo fileInfo;
-
-        /// a buffer for all plain-text content generated by strigi
-        std::string content;
-
-    private:
-        /// The Strigi result
-        const Strigi::AnalysisResult* m_analysisResult;
-    };
-
-
-    Soprano::Node RegisteredFieldData::createObject( const std::string& value )
-    {
-        //
-        // Strigi uses anonymeous nodes prefixed with ':'. However, it is possible that literals
-        // start with a ':'. Thus, we also check the range of the property
-        //
-        if ( value[0] == ':' &&
-             m_property.range().isValid() ) {
-            return createBlankOrResourceNode( value );
-        }
-
-        //
-        // We handle only one special case here: relations to other files
-        //
-        else if( m_property.range().isValid() &&
-                QFile::exists(QFile::decodeName(value.c_str())) ) {
-            Nepomuk::Resource fileRes(KUrl::fromLocalFile(QFile::decodeName(value.c_str())));
-            if( fileRes.exists() ) {
-                return Soprano::Node( fileRes.resourceUri() );
-            }
-            else {
-                kDebug() << "Cannot resolve local file path" << value.c_str() << "to a file resource!";
-                return Soprano::Node();
-            }
-        }
-
-        //
-        // fallback to literals
-        //
-        else {
-            return createLiteralValue( value );
-        }
-    }
-
-
-    Soprano::LiteralValue RegisteredFieldData::createLiteralValue( const std::string& value )
-    {
-        QString s = QString::fromUtf8( ( const char* )value.c_str(), value.length() ).trimmed();
-        if( s.isEmpty() )
-            return Soprano::LiteralValue();
-
-        // This is a workaround for a Strigi bug which sometimes stores datatime values as strings
-        // but the format is not supported by Soprano::LiteralValue
-        if ( m_dataType == QVariant::DateTime ) {
-            // dateTime is stored as integer (time_t) in strigi
-            bool ok = false;
-            uint t = s.toUInt( &ok );
-            if ( ok ) {
-                // workaround for id3 tags which might only have a year encoded
-                if ( t >= 1900 && t <= 9999 )
-                    return LiteralValue( QDateTime( QDate(t, 1, 1), QTime(0, 0), Qt::UTC ) );
-                else
-                    return LiteralValue( QDateTime::fromTime_t( t ) );
-            }
-
-            // workaround for at least nie:contentCreated which is encoded like this: "2005:06:03 17:13:33"
-            QDateTime dt = QDateTime::fromString( s, QLatin1String( "yyyy:MM:dd hh:mm:ss" ) );
-            if ( dt.isValid() )
-                return LiteralValue( dt );
-        }
-
-        // this is a workaround for EXIF values stored as "1/10" and the like which need to
-        // be converted to double values.
-        else if ( m_dataType == QVariant::Double ) {
-            bool ok = false;
-            double d = s.toDouble( &ok );
-            if ( ok )
-                return LiteralValue( d );
-
-            int x = 0;
-            int y = 0;
-            if ( sscanf( s.toLatin1().data(), "%d/%d", &x, &y ) == 2 ) {
-                return LiteralValue( double( x )/double( y ) );
-            }
-        }
-
-        if ( m_dataType != QVariant::Invalid ) {
-            return LiteralValue::fromString( s, m_dataType );
-        }
-        else {
-            // we default to string
-            return LiteralValue( s );
-        }
-    }
-
-
-    FileMetaData::FileMetaData( const Strigi::AnalysisResult* idx )
-        : m_analysisResult( idx )
-    {
-        fileUrl = createFileUrl( idx );
-        fileInfo = QFileInfo( fileUrl.toLocalFile() );
-
-        // determine the resource URI by using Nepomuk::Resource's power
-        // this will automatically find previous uses of the file in question
-        // with backwards compatibility
-        resourceUri = Nepomuk::Resource( fileUrl ).resourceUri();
-    }
-
-    void FileMetaData::storeBasicData( Nepomuk::IndexFeeder * feeder )
-    {
-        feeder->addStatement( resourceUri, Nepomuk::Vocabulary::NIE::url(), fileUrl );
-
-        if ( fileInfo.exists() ) {
-            // Strigi only indexes files and extractors mostly (if at all) store the nie:DataObject type (i.e. the contents)
-            // Thus, here we go the easy way and mark each indexed file as a nfo:FileDataObject.
-            feeder->addStatement( resourceUri,
-                                  Vocabulary::RDF::type(),
-                                  Nepomuk::Vocabulary::NFO::FileDataObject() );
-            if ( fileInfo.isDir() ) {
-                feeder->addStatement( resourceUri,
-                                      Vocabulary::RDF::type(),
-                                      Nepomuk::Vocabulary::NFO::Folder() );
-            }
-        }
-    }
-
-    FileMetaData* fileDataForResult( const Strigi::AnalysisResult* idx )
-    {
-        return static_cast<FileMetaData*>( idx->writerData() );
-    }
-}
-
-
-class Nepomuk::StrigiIndexWriter::Private
-{
-public:
-    //
-    // The Strigi API does not provide context information in addTriplet, i.e. the AnalysisResult.
-    // However, we only use one thread, only one AnalysisResult at the time.
-    // Thus, we can just remember that and use it in addTriplet.
-    //
-    QMutex resultStackMutex;
-    QStack<const Strigi::AnalysisResult*> currentResultStack;
-
-    Nepomuk::IndexFeeder* feeder;
-};
-
-
-Nepomuk::StrigiIndexWriter::StrigiIndexWriter( IndexFeeder* feeder )
-    : Strigi::IndexWriter(),
-    d( new Private() )
-{
-    d->feeder = feeder;
-}
-
-
-Nepomuk::StrigiIndexWriter::~StrigiIndexWriter()
-{
-    kDebug();
-    delete d;
-}
-
-
-// unused
-void Nepomuk::StrigiIndexWriter::commit()
-{
-    // do nothing
-}
-
-
-// delete all indexed data for the files listed in entries
-void Nepomuk::StrigiIndexWriter::deleteEntries( const std::vector<std::string>& entries )
-{
-    for ( unsigned int i = 0; i < entries.size(); ++i ) {
-        QString path = QString::fromUtf8( entries[i].c_str() );
-        IndexFeeder::clearIndexedDataForUrl( KUrl( path ) );
-    }
-}
-
-
-// unused
-void Nepomuk::StrigiIndexWriter::deleteAllEntries()
-{
-    // do nothing
-}
-
-
-// called for each indexed file
-void Nepomuk::StrigiIndexWriter::startAnalysis( const AnalysisResult* idx )
-{
-    // we need to remember the AnalysisResult since addTriplet does not provide it
-    d->currentResultStack.push(idx);
-
-    // for now we ignore embedded files -> too many false positives and useless query results
-    if ( idx->depth() > 0 ) {
-        return;
-    }
-
-    // create the file data used during the analysis
-    FileMetaData* data = new FileMetaData( idx );
-
-    // remove previously indexed data
-    IndexFeeder::clearIndexedDataForResourceUri( data->resourceUri );
-
-    // It is important to keep the resource URI between updates (especially for sharing of files)
-    // However, when updating data from pre-KDE 4.4 times we want to get rid of old file:/ resource
-    // URIs. However, we can only do that if we were the only ones to write info about that file
-    // Thus, we need to use Nepomuk::Resource again
-    if ( data->resourceUri.scheme() == QLatin1String( "file" ) ) {
-        // we need to clear the ResourceManager cache. Otherwise the bug/shortcoming in libnepomuk will
-        // always get us back to the cached file:/ URI
-        Nepomuk::ResourceManager::instance()->clearCache();
-        data->resourceUri = Nepomuk::Resource( data->fileUrl ).resourceUri();
-    }
-
-    // create a new resource URI for non-existing file resources
-    if ( data->resourceUri.isEmpty() )
-        data->resourceUri = Nepomuk::ResourceManager::instance()->generateUniqueUri( QString() );
-
-    // Initialize the feeder to accept statements
-    d->feeder->begin( data->resourceUri );
-
-    // store initial data to make sure newly created URIs are reused directly by libnepomuk
-    data->storeBasicData( d->feeder );
-
-    // remember the file data
-    idx->setWriterData( data );
-}
-
-
-void Nepomuk::StrigiIndexWriter::addText( const AnalysisResult* idx, const char* text, int32_t length )
-{
-    if ( idx->depth() > 0 ) {
-        return;
-    }
-
-    FileMetaData* md = fileDataForResult( idx );
-    md->content.append( text, length );
-}
-
-
-void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
-                                           const RegisteredField* field,
-                                           const std::string& value )
-{
-    if ( idx->depth() > 0 ) {
-        return;
-    }
-
-    if ( value.length() > 0 ) {
-        FileMetaData* md = fileDataForResult( idx );
-        RegisteredFieldData* rfd = reinterpret_cast<RegisteredFieldData*>( field->writerData() );
-
-        // the statement we will create, we will determine the object below
-        Soprano::Statement statement( md->resourceUri, rfd->property().uri(), Soprano::Node() );
-
-        //
-        // Strigi uses rdf:type improperly since it stores the value as a string. We have to
-        // make sure it is a resource.
-        // only we handle the basic File/Folder typing ourselves
-        //
-        if ( rfd->isRdfType() ) {
-            const QUrl type = QUrl::fromEncoded( value.c_str(), QUrl::StrictMode );
-            if ( statement.object().uri() != Nepomuk::Vocabulary::NFO::FileDataObject() ) {
-                statement.setObject( type );
-            }
-        }
-
-        //
-        // parent location is a special case as we need the URI of the corresponding resource instead of the path
-        //
-        else if ( field->key() == FieldRegister::parentLocationFieldName ) {
-            QUrl folderUri = determineFolderResourceUri( QUrl::fromLocalFile( QFile::decodeName( QByteArray::fromRawData( value.c_str(), value.length() ) ) ) );
-            if ( !folderUri.isEmpty() )
-                statement.setObject( folderUri );
-        }
-
-        //
-        // ignore the path as we handle that ourselves in startAnalysis
-        //
-        else if ( field->key() != FieldRegister::pathFieldName ) {
-            statement.setObject( rfd->createObject( value ) );
-        }
-
-        if ( !statement.object().isEmpty() ) {
-            d->feeder->addStatement( statement );
-        }
-    }
-}
-
-
-// the main addValue method
-void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
-                                           const RegisteredField* field,
-                                           const unsigned char* data,
-                                           uint32_t size )
-{
-    addValue( idx, field, std::string( ( const char* )data, size ) );
-}
-
-
-void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult*, const RegisteredField*,
-                                           const std::string&, const std::string& )
-{
-    // we do not support map types
-}
-
-
-void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
-                                           const RegisteredField* field,
-                                           uint32_t value )
-{
-    if ( idx->depth() > 0 ) {
-        return;
-    }
-
-    FileMetaData* md = fileDataForResult( idx );
-    RegisteredFieldData* rfd = reinterpret_cast<RegisteredFieldData*>( field->writerData() );
-
-    LiteralValue val( value );
-    if ( field->type() == FieldRegister::datetimeType ) {
-        val = QDateTime::fromTime_t( value );
-    }
-
-    d->feeder->addStatement( md->resourceUri, rfd->property().uri(), val);
-}
-
-
-void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
-                                           const RegisteredField* field,
-                                           int32_t value )
-{
-    if ( idx->depth() > 0 ) {
-        return;
-    }
-
-    FileMetaData* md = fileDataForResult( idx );
-    RegisteredFieldData* rfd = reinterpret_cast<RegisteredFieldData*>( field->writerData() );
-
-    d->feeder->addStatement( md->resourceUri, rfd->property().uri(), LiteralValue( value ) );
-}
-
-
-void Nepomuk::StrigiIndexWriter::addValue( const AnalysisResult* idx,
-                                           const RegisteredField* field,
-                                           double value )
-{
-    if ( idx->depth() > 0 ) {
-        return;
-    }
-
-    FileMetaData* md = fileDataForResult( idx );
-    RegisteredFieldData* rfd = reinterpret_cast<RegisteredFieldData*>( field->writerData() );
-
-    d->feeder->addStatement( md->resourceUri, rfd->property().uri(), LiteralValue( value ) );
-}
-
-
-void Nepomuk::StrigiIndexWriter::addTriplet( const std::string& s,
-                                             const std::string& p,
-                                             const std::string& o )
-{
-    if ( d->currentResultStack.top()->depth() > 0 ) {
-        return;
-    }
-    //FileMetaData* md = fileDataForResult( d->currentResultStack.top() );
-
-    Soprano::Node subject( createBlankOrResourceNode( s ) );
-    Nepomuk::Types::Property property( QUrl( QString::fromUtf8(p.c_str()) ) ); // Was mapped earlier
-    Soprano::Node object;
-    if ( property.range().isValid() )
-        object = Soprano::Node( createBlankOrResourceNode( o ) );
-    else
-        object = Soprano::LiteralValue::fromString( QString::fromUtf8( o.c_str() ), property.literalRangeType().dataTypeUri() );
-    
-    if( object.isValid() )
-        d->feeder->addStatement( subject, property.uri(), object );
-    else {
-        kDebug() << QString::fromUtf8( o.c_str() ) << " could not be parsed as a " << property.literalRangeType().dataTypeUri();
-    }
-        
-}
-
-
-// called after each indexed file
-void Nepomuk::StrigiIndexWriter::finishAnalysis( const AnalysisResult* idx )
-{
-    d->currentResultStack.pop();
-
-    if ( idx->depth() > 0 ) {
-        return;
-    }
-
-    FileMetaData* md = fileDataForResult( idx );
-
-    // store the full text of the file
-    if ( md->content.length() > 0 ) {
-        d->feeder->addStatement( md->resourceUri,
-                                 Nepomuk::Vocabulary::NIE::plainTextContent(),
-                                 LiteralValue( QString::fromUtf8( md->content.c_str() ) ) );
-    }
-
-    d->feeder->end( md->fileInfo.isDir() );
-
-    // cleanup
-    delete md;
-    idx->setWriterData( 0 );
-}
-
-
-void Nepomuk::StrigiIndexWriter::initWriterData( const Strigi::FieldRegister& f )
-{
-    // build a temp hash for built-in strigi types
-    QHash<std::string, QVariant::Type> literalTypes;
-    literalTypes[FieldRegister::stringType] = QVariant::String;
-    literalTypes[FieldRegister::floatType] = QVariant::Double;
-    literalTypes[FieldRegister::integerType] = QVariant::Int;
-    literalTypes[FieldRegister::binaryType] = QVariant::ByteArray;
-    literalTypes[FieldRegister::datetimeType] = QVariant::DateTime; // Strigi encodes datetime as unsigned integer, i.e. addValue( ..., uint )
-
-    // cache type conversion for all strigi fields
-    std::map<std::string, RegisteredField*>::const_iterator i;
-    std::map<std::string, RegisteredField*>::const_iterator end = f.fields().end();
-    for (i = f.fields().begin(); i != end; ++i) {
-        Nepomuk::Types::Property prop = Strigi::Util::fieldUri( i->second->key() );
-
-        QVariant::Type type( QVariant::Invalid );
-
-        QHash<std::string, QVariant::Type>::const_iterator it = literalTypes.constFind( i->second->properties().typeUri() );
-        if ( it != literalTypes.constEnd() ) {
-            type = *it;
-        }
-        else if ( prop.literalRangeType().isValid() ) {
-            type = LiteralValue::typeFromDataTypeUri( prop.literalRangeType().dataTypeUri() );
-        }
-
-        //kDebug() << prop << type;
-
-        i->second->setWriterData( new RegisteredFieldData( prop, type ) );
-    }
-}
-
-
-void Nepomuk::StrigiIndexWriter::releaseWriterData( const Strigi::FieldRegister& f )
-{
-    std::map<std::string, RegisteredField*>::const_iterator i;
-    std::map<std::string, RegisteredField*>::const_iterator end = f.fields().end();
-    for (i = f.fields().begin(); i != end; ++i) {
-        delete static_cast<RegisteredFieldData*>( i->second->writerData() );
-        i->second->setWriterData( 0 );
-    }
-}
-
-
-QUrl Nepomuk::StrigiIndexWriter::determineFolderResourceUri( const KUrl& fileUrl )
-{
-    Nepomuk::Resource res( fileUrl );
-    if ( res.exists() ) {
-        return res.resourceUri();
-    }
-    else {
-        kDebug() << "Could not find resource URI for folder (this is not an error)" << fileUrl;
-        return QUrl();
-    }
-}
diff --git a/nepomuk/services/strigi/nepomukindexwriter.h b/nepomuk/services/strigi/nepomukindexwriter.h
deleted file mode 100644
index cdda8c9..0000000
--- a/nepomuk/services/strigi/nepomukindexwriter.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-  Copyright (C) 2007-2010 Sebastian Trueg <trueg@kde.org>
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU 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 library; see the file COPYING.  If not, write to
-  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-  Boston, MA 02110-1301, USA.
-*/
-
-#ifndef _STRIGI_NEPOMUK_INDEX_WRITER_H_
-#define _STRIGI_NEPOMUK_INDEX_WRITER_H_
-
-#include <strigi/indexwriter.h>
-#include <strigi/analysisresult.h>
-#include <strigi/analyzerconfiguration.h>
-
-class KUrl;
-class QUrl;
-
-namespace Nepomuk {
-
-    class Resource;
-    class IndexFeeder;
-
-    class StrigiIndexWriter : public Strigi::IndexWriter
-    {
-    public:
-        StrigiIndexWriter( IndexFeeder* );
-        ~StrigiIndexWriter();
-
-        void commit();
-
-        /**
-         * Delete the entries with the given paths from the index.
-         *
-         * @param entries the paths of the files that should be deleted
-         **/
-        void deleteEntries( const std::vector<std::string>& entries );
-
-        /**
-         * Delete all indexed documents from the index.
-         **/
-        void deleteAllEntries();
-
-        void initWriterData( const Strigi::FieldRegister& );
-        void releaseWriterData( const Strigi::FieldRegister& );
-
-        void startAnalysis( const Strigi::AnalysisResult* );
-        void addText( const Strigi::AnalysisResult*, const char* text, int32_t length );
-        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
-                       const std::string& value );
-        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
-                       const unsigned char* data, uint32_t size );
-        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
-                       int32_t value );
-        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
-                       uint32_t value );
-        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
-                       double value );
-        void addTriplet( const std::string& subject,
-                         const std::string& predicate, const std::string& object );
-        void addValue( const Strigi::AnalysisResult*, const Strigi::RegisteredField* field,
-                       const std::string& name, const std::string& value );
-        void finishAnalysis( const Strigi::AnalysisResult* );
-
-    private:
-        QUrl determineFolderResourceUri( const KUrl& fileUrl );
-
-        class Private;
-        Private* d;
-    };
-}
-
-uint qHash( const std::string& s );
-
-#endif
diff --git a/nepomuk/services/strigi/nepomukstrigiservice.desktop b/nepomuk/services/strigi/nepomukstrigiservice.desktop
index 34cd944..961e787 100644
--- a/nepomuk/services/strigi/nepomukstrigiservice.desktop
+++ b/nepomuk/services/strigi/nepomukstrigiservice.desktop
@@ -12,6 +12,7 @@ Name[be@latin]=Słužba „Nepomuk”/„Strigi”
 Name[bg]=Услуга Nepomuk Strigi
 Name[bn]=নেপোমুক স্ট্রিগি সার্ভিস
 Name[bn_IN]=Nepomuk Strigi পরিসেবা
+Name[bs]=Nepomukov servis Strigija
 Name[ca]=Servei Strigi del Nepomuk
 Name[ca@valencia]=Servei Strigi del Nepomuk
 Name[cs]=Nepomuk Strigi služba
@@ -75,6 +76,7 @@ Name[te]=Nepomuk Strigi సేవ
 Name[tg]=Хидматҳои Nepomuk Strigi
 Name[th]=บริการ Nepomuk Strigi
 Name[tr]=Nepomuk Strigi Servisi
+Name[ug]=Nepomuk Strigi مۇلازىمىتى
 Name[uk]=Служба Strigi Nepomuk
 Name[uz]=Nepomuk Strigi xizmati
 Name[uz@cyrillic]=Nepomuk Strigi хизмати
@@ -87,6 +89,7 @@ Comment[ar]=خدمة نبومك المتحكمة بمراقب ستريجي ال
 Comment[ast]=Serviciu Nepomuk que remana'l strigidaemon, ex. Indexa ficheros del escritoriu
 Comment[be@latin]=Słužba „Nepomuk”, jakaja kiruje prahramaj „strigidaemon”, indeksujučy fajły ŭ kamputary
 Comment[bg]=Услуга, която контролира демона на Strigi, напр. файлове с индекси
+Comment[bs]=Servis Nepomuka za upravljanje demonom Strigija, tj. indeksiranje datoteka na površi
 Comment[ca]=Servei del Nepomuk que controla el strigidaemon, p.ex. indexa els fitxers de l'escriptori
 Comment[ca@valencia]=Servei del Nepomuk que controla el strigidaemon, p.ex. indexa els fitxers de l'escriptori
 Comment[cs]=Služba Nepomuku ovládající strigidaemon, který indexuje data v počítači
@@ -146,6 +149,7 @@ Comment[ta]=Nepomuk Service which controls the strigidaemon, i.e. indexes files
 Comment[te]=Nepomuk సేవ strigidaemon ను నియత్రిస్తుంది, అంటే. డెస్‍క్‌టాప్ పైన దస్త్రములను క్రమపరుస్తుంది
 Comment[th]=บริการของ Nepomuk สำหรับสำหรับควบคุมดีมอน strigidaemon ที่ทำหน้าที่ เช่น สร้างดัชนีแฟ้มต่าง ๆ บนพื้นที่ทำงาน เป็นต้น
 Comment[tr]=Strigidaemon uygulamasını yöneten Nepomuk servisi, masaüstünüzdeki dosyaları indeksleyen bir uygulama
+Comment[ug]=Nepomuk مۇلازىمىتى strigidaemon تىزگىنلەيدۇ، سىستېمىدىكى ھۆججەتنى ئىندېكىسلايدۇ
 Comment[uk]=Служба Nepomuk, яка контролює фонову службу strigi, тобто, індексує файли на стільниці
 Comment[wa]=Siervice Nepomuk ki controle li démon strigi, ki fwait ls indecses des fitchîs sol sicribanne
 Comment[x-test]=xxNepomuk Service which controls the strigidaemon, i.e. indexes files on the desktopxx
diff --git a/nepomuk/services/strigi/nepomukstrigiservice.notifyrc b/nepomuk/services/strigi/nepomukstrigiservice.notifyrc
index 5d952d0..baab4e6 100644
--- a/nepomuk/services/strigi/nepomukstrigiservice.notifyrc
+++ b/nepomuk/services/strigi/nepomukstrigiservice.notifyrc
@@ -5,6 +5,7 @@ Comment[ar]=بحث سطح المكتب
 Comment[ast]=Guetador d'escritoriu
 Comment[bg]=Настолно търсене
 Comment[bn]=ডেস্কটপ সন্ধান
+Comment[bs]=Pretraga površi
 Comment[ca]=Cerca a l'escriptori
 Comment[ca@valencia]=Cerca a l'escriptori
 Comment[cs]=Vyhledávací služby
@@ -34,7 +35,6 @@ Comment[kn]=ಗಣಕತೆರೆ ಹುಡುಕಾಟ
 Comment[ko]=데스크톱 검색
 Comment[lt]=Darbastalio paieška
 Comment[lv]=Darbvirsmas meklēšana
-Comment[mai]=डेस्कटॉप खोज
 Comment[ml]=പണിയിട തിരയല്‍
 Comment[nb]=Skrivebordssøk
 Comment[nds]=Schriefdischsöök
@@ -57,6 +57,7 @@ Comment[sv]=Skrivbordssökning
 Comment[tg]=Ҷустуҷӯи мизи корӣ
 Comment[th]=ค้นหาผ่านพื้นที่ทำงาน
 Comment[tr]=Masaüstü Araması
+Comment[ug]=ئۈستەلئۈستىدە ئىزدەش
 Comment[uk]=Стільничний пошук
 Comment[wa]=Cweraedje sol sicribanne
 Comment[x-test]=xxDesktop Searchxx
@@ -70,6 +71,7 @@ Name[ast]=Aniciada la indexación inicial
 Name[be@latin]=Pačałosia pačatkovaje indeksavańnie.
 Name[bg]=Започнато е индексиране
 Name[bn]=প্রাথমিক সূচি তৈরী করা শুরু
+Name[bs]=Započeto početno indeksiranje
 Name[ca]=S'ha engegat la indexació inicial
 Name[ca@valencia]=S'ha engegat la indexació inicial
 Name[cs]=Úvodní indexování zahájeno
@@ -134,6 +136,7 @@ Name[te]=ప్రాధమిక విషయవర్గీకరణ ప్ర
 Name[tg]=знак пунктуации, открывающая кавычка
 Name[th]=เริ่มเตรียมการทำดัชนี
 Name[tr]=Temel İndeksleme başladı
+Name[ug]=ئىندېكىسلاشنى دەسلەپلەشتۈرۈش باشلاندى
 Name[uk]=Запущено початкове індексування
 Name[wa]=Li prumî indecsaedje a cmincî
 Name[x-test]=xxInitial Indexing startedxx
@@ -142,6 +145,8 @@ Name[zh_TW]=初始化索引已開始
 Comment=Indexing of local files for fast searches has started.
 Comment[ar]=بدات سراتجي في الفهرسة الأولية للملفات المحلية من أجل البحث السريع
 Comment[ast]=Anicióse indexación de ficheros llocales pa guetes rápides.
+Comment[bg]=Индексиране на локални файлове за по-бързо търсене
+Comment[bs]=Započeto je indeksiranje lokalnih datoteka radi brze pretrage.
 Comment[ca]=S'ha engegat la indexació dels fitxers locals per a les cerques ràpides.
 Comment[ca@valencia]=S'ha engegat la indexació dels fitxers locals per a les cerques ràpides.
 Comment[cs]=Bylo zahájeno indexování místních souborů pro rychlé vyhledávání.
@@ -158,7 +163,7 @@ Comment[fi]=Paikallisten tiedostojen indeksointi pikahakua varten on käynnistyn
 Comment[fr]=L'indexation des fichiers locaux pour des recherches rapides a commencé.
 Comment[fy]=Yndeksearring fan lokale triemmen foar flugge buroblêd-sykopdrachten is úteinsetten.
 Comment[ga]=Tosaíodh innéacsú de chomhaid logánta a cheadaíonn cuardaigh níos tapúla.
-Comment[gl]=Comezouse a indexación dos ficheiros locais para facer procuras rápidas.
+Comment[gl]=Comezouse a indexación dos ficheiros locais para facer buscas rápidas.
 Comment[he]=החל מפתוח של קבצים מקומיים לצורך חיפוש מהיר.
 Comment[hr]=Započelo je indeksiranje lokalnih datoteka za brzu pretragu.
 Comment[hu]=A helyi fájlok indexelése a keresések gyorsításához elkezdődött.
@@ -192,8 +197,9 @@ Comment[sr@ijekavian]=Започето је индексирање локалн
 Comment[sr@ijekavianlatin]=Započeto je indeksiranje lokalnih fajlova radi brze pretrage.
 Comment[sr@latin]=Započeto je indeksiranje lokalnih fajlova radi brze pretrage.
 Comment[sv]=Indexering av lokala filer för snabb sökning har startat.
-Comment[th]=การทำดัชนีของแฟ้มต่าง ๆ ภายในระบบเพื่อการค้นหาที่รวดเร็วได้เริ่มแล้ว
+Comment[th]=การทำดัชนีของแฟ้มต่างๆ ภายในระบบเพื่อการค้นหาที่รวดเร็วได้เริ่มแล้ว
 Comment[tr]=Hızlı arama için yerel dosyaların indekslenme işlemi başlatıldı.
+Comment[ug]=يەرلىك ھۆججەتنى تېز ئىزدەش ئۈچۈن ئىندېكسلاش باشلاندى.
 Comment[uk]=Розпочато індексування локальних файлів для пришвидшення пошуку.
 Comment[x-test]=xxIndexing of local files for fast searches has started.xx
 Comment[zh_CN]=本地文件索引已经开始。
@@ -205,8 +211,9 @@ Name=Initial Indexing finished
 Name[ar]=الفهرسة الأولية انتهت
 Name[ast]=Finó indexación inicial
 Name[be@latin]=Skončyłasia pačatkovaje indeksavańnie.
-Name[bg]=Индексирането завърши
+Name[bg]=Началното индексиране завърши
 Name[bn]=প্রাথমিক সূচি তৈরী করা সম্পন্ন
+Name[bs]=Završeno početno indeksiranje
 Name[ca]=Ha acabat la indexació inicial
 Name[ca@valencia]=Ha acabat la indexació inicial
 Name[cs]=Úvodní indexování dokončeno
@@ -271,6 +278,7 @@ Name[te]=ప్రాధమిక విషయవర్గీకరణ పూర
 Name[tg]=Эҷоди индекс ба итмом расид
 Name[th]=เตรียมการการทำดัชนีเสร็จแล้ว
 Name[tr]=Temel İndeksleme tamamlandı
+Name[ug]=ئىندېكىسلاشنى دەسلەپلەشتۈرۈش تاماملاندى
 Name[uk]=Початкове індексування закінчено
 Name[x-test]=xxInitial Indexing finishedxx
 Name[zh_CN]=索引初始化完成
@@ -278,6 +286,8 @@ Name[zh_TW]=初始化索引已完成
 Comment=The initial indexing of local files for fast desktop searches has completed.
 Comment[ar]=انتهت سراتجي من الفهرسة الأولية للملفات المحلية من أجل البحث السريع
 Comment[ast]=Completóse indexación inicial de ficheros llocales pa guetes rápides d'escritoriu.
+Comment[bg]=Приключи началното индексиране на локални файлове за по-бързо търсене
+Comment[bs]=Završeno je početno indeksiranje lokalnih datoteka radi brze pretrage površi.
 Comment[ca]=Ha finalitzat la indexació inicial dels fitxers locals per a les cerques ràpides d'escriptori.
 Comment[ca@valencia]=Ha finalitzat la indexació inicial dels fitxers locals per a les cerques ràpides d'escriptori.
 Comment[cs]=Úvodní indexování místních souborů pro rychlé hledání bylo dokončeno.
@@ -294,7 +304,7 @@ Comment[fi]=Paikallisten tiedostojen alustava indeksointi työpöydän pikahakua
 Comment[fr]=L'indexation initiale des fichiers locaux pour des recherches rapides est terminée.
 Comment[fy]=De earste yndeksearring fan lokale triemmen foar flugge buroblêd-sykopdrachten is foltôge.
 Comment[ga]=Tá an chéad innéacsú de chomhaid logánta críochnaithe.
-Comment[gl]=Completouse a indexación inicial dos ficheiros locais para facer procuras rápidas no escritorio.
+Comment[gl]=Completouse a indexación inicial dos ficheiros locais para facer buscas rápidas no escritorio.
 Comment[he]=הסתיים המפתוח הראשוני של קבצים מקומיים לצורך חיפוש מהיר בשולחן העבודה.
 Comment[hr]=Završilo je prvotno indeksiranje lokalnih datoteka za brzu pretragu računala.
 Comment[hu]=A helyi fájlok kezdeti indexe a gyorsabb asztali kereséshez elkészült.
@@ -331,6 +341,7 @@ Comment[sr@latin]=Završeno je početno indeksiranje lokalnih fajlova radi brze
 Comment[sv]=Den inledande indexeringen av lokala filer för snabb skrivbordssökning är klar.
 Comment[th]=การทำดัชนีของแฟ้มภายในระบบเพื่อการค้นหาที่รวดเร็วเสร็จสมบูรณ์แล้ว
 Comment[tr]=Hızlı masaüstü araması için yerel dosyaların temel indeksleme işlemi tamamlandı.
+Comment[ug]=يەرلىك ھۆججەتنى ئۈستەلئۈستىدە تېز ئىزدەش ئۈچۈن ئىندېكىسلاشنى دەسلەپلەشتۈرۈش تاماملاندى.
 Comment[uk]=Початкове індексування локальних файлів для пришвидшення стільничного пошуку завершено.
 Comment[x-test]=xxThe initial indexing of local files for fast desktop searches has completed.xx
 Comment[zh_CN]=Strigi 对本地文件的索引初始化工作已完成。
@@ -344,6 +355,7 @@ Name[ast]=Indexación encaboxada
 Name[be@latin]=Indeksavańnie spynienaje
 Name[bg]=Индексирането е прекъснато
 Name[bn]=সূচি তৈরী করা স্থগিত
+Name[bs]=Indeksiranje suspendovano
 Name[ca]=La indexació s'ha suspès
 Name[ca@valencia]=La indexació s'ha suspès
 Name[cs]=Indexování pozastaveno
@@ -408,6 +420,7 @@ Name[te]=విషయవర్దీకరణ సంస్పెండ్ చే
 Name[tg]=Indexing suspended
 Name[th]=การทำดัชนีถูกหยุดพักไว้ชั่วคราว
 Name[tr]=İndeksleme beklemeye alındı
+Name[ug]=ئىندېكسلاش ۋاقتىنچە توختىتىلدى
 Name[uk]=Пауза індексування
 Name[wa]=Indecsaedje djoké
 Name[x-test]=xxIndexing suspendedxx
@@ -416,6 +429,8 @@ Name[zh_TW]=索引暫停
 Comment=File indexing has been suspended by the search service.
 Comment[ar]=خدمة البحث علّقت فهرسة الملفات
 Comment[ast]=El serviciu de gueta suspendió la indexación de ficheros.
+Comment[bg]=Индексирането на файлове е прекъснато от услугата за търсене.
+Comment[bs]=Servis pretrage je suspendovao indeksiranje datoteka.
 Comment[ca]=El servei de cerca ha suspès la indexació de fitxers.
 Comment[ca@valencia]=El servei de cerca ha suspès la indexació de fitxers.
 Comment[cs]=Indexování souborů vyhledávací službou bylo pozastaveno.
@@ -432,7 +447,7 @@ Comment[fi]=Hakupalvelu on keskeyttänyt tiedostoindeksoinnin.
 Comment[fr]=L'indexation de fichiers a été suspendue par le service de recherche.
 Comment[fy]=Triem yndeksearring is ûnderbrútsen troch de syk tsjinst
 Comment[ga]=Chuir an tseirbhís chuardaigh innéacsú comhad ar fionraí.
-Comment[gl]=O servizo de procuras suspendeu a indexación de ficheiros.
+Comment[gl]=O servizo de buscas suspendeu a indexación de ficheiros.
 Comment[he]=מפתוח הקבצים הושהה על־ידי שירות־החיפוש.
 Comment[hr]=Usluga pretrage je pauzirala indeksiranje datoteka.
 Comment[hu]=A keresőszolgáltatás felfüggesztette a fájlindexelést.
@@ -447,7 +462,6 @@ Comment[kn]=ಹುಡುಕು ಸೇವೆಯಿಂದ ಕಡತಗಳ ಸೂ
 Comment[ko]=검색 서비스에서 파일 인덱싱을 중지하였습니다.
 Comment[lt]=Paieškos tarnyba sustabdė failų indeksavimą.
 Comment[lv]=Failu indeksēšanu apturēja meklēšanas serviss.
-Comment[mai]=स्ट्रिगी फाइल सूची बनैनाइ निलंबित कएल गेल अछि
 Comment[mk]=Индексирањето на датотеки е привремено прекинато од сервисот за пребарување.
 Comment[ml]=തെരച്ചില്‍ സേവനം ഫയല്‍ സ്വാംശീകരണം താല്‍കാലികമായി നിര്‍ത്തിയിരിയ്ക്കുന്നു.
 Comment[nb]=Søketjenesten har stoppet filindeksering midlertidig.
@@ -469,6 +483,7 @@ Comment[sr@latin]=Servis pretrage je suspendovao indeksiranje fajlova.
 Comment[sv]=Filindexering har tillfälligt stoppats av söktjänsten.
 Comment[th]=การทำดัชนีถูกหยุดพักชั่วคราวจากบริการค้นหา
 Comment[tr]=Dosya indeksleyici arama servisi tarafından durduruldu.
+Comment[ug]=ھۆججەت ئىندېكسلاش ئىزدەش مۇلازىمىتى تەرىپىدىن ۋاقتىنچە توختىتىلدى.
 Comment[uk]=Індексування файлів було призупинено службою пошуку.
 Comment[x-test]=xxFile indexing has been suspended by the search service.xx
 Comment[zh_CN]=Strigi 文件索引操作已经被搜索服务推迟。
@@ -482,6 +497,7 @@ Name[ast]=Indexación reanudada
 Name[be@latin]=Indeksavańnie praciahnutaje
 Name[bg]=Индексирането е продължено
 Name[bn]=সূচি তৈরী করা পুনরায় চালু
+Name[bs]=Indeksiranje nastavljeno
 Name[ca]=La indexació s'ha reprès
 Name[ca@valencia]=La indexació s'ha représ
 Name[cs]=Indexování obnoveno
@@ -546,6 +562,7 @@ Name[te]=విషయవర్గీకరణ తిరిగికొనసా
 Name[tg]=Indexing resumed
 Name[th]=การทำดัชนีกลับมาทำงานต่อแล้ว
 Name[tr]=İndeksleme çalışmaya devam ettirildi
+Name[ug]=ئىندېكسلاش داۋاملاشتۇرۇلدى
 Name[uk]=Продовження індексування
 Name[x-test]=xxIndexing resumedxx
 Name[zh_CN]=索引已恢复
@@ -553,6 +570,8 @@ Name[zh_TW]=索引回復
 Comment=File indexing has been resumed by the search service.
 Comment[ar]=استأنفت علمية الفهرسة
 Comment[ast]=El serviciu de gueta reanudó la indexación de ficheros.
+Comment[bg]=Индексирането на файлове е продължено от услугата за търсене
+Comment[bs]=Servis pretrage je nastavio indeksiranje datoteka.
 Comment[ca]=El servei de cerca ha reprès la indexació de fitxers.
 Comment[ca@valencia]=El servei de cerca ha représ la indexació de fitxers.
 Comment[cs]=Indexování souborů vyhledávací službou bylo obnoveno.
@@ -569,7 +588,7 @@ Comment[fi]=Hakupalvelu on käynnistänyt tiedostoindeksoinnin uudelleen
 Comment[fr]=L'indexation des fichiers a été reprise par le service de recherche.
 Comment[fy]=Triem yndeksearring is ferfette troch de syk tsjinst
 Comment[ga]=D'atosaigh an tseirbhís chuardaigh innéacsú comhad.
-Comment[gl]=O servizo de procuras continuou a indexación de ficheiros.
+Comment[gl]=O servizo de buscas continuou a indexación de ficheiros.
 Comment[he]=מפתוח הקבצים חזר לפעול על־ידי שירות־החיפוש.
 Comment[hr]=Usluga pretrage je nastavila indeksiranje datoteka.
 Comment[hu]=A keresőszolgáltatás újraindította a fájlindexelést.
@@ -584,7 +603,6 @@ Comment[kn]=ಹುಡುಕು ಸೇವೆಯಿಂದ ಕಡತಗಳ ಸೂ
 Comment[ko]=검색 서비스에서 파일 인덱싱을 다시 시작하였습니다.
 Comment[lt]=Paieškos tarnyba atnaujino failų indeksavimą.
 Comment[lv]=Failu indeksēšanu atsāka meklēšanas serviss.
-Comment[mai]=स्ट्रिगी फाइल सूची बनैनाइ पुनरारंभ
 Comment[mk]=Индексирањето на датотеки е продолжено од сервисот за пребарување.
 Comment[ml]=തെരച്ചില്‍ സേവനം ഫയല്‍ സ്വാംശീകരണം തുടരുന്നു.
 Comment[nb]=Søketjenesten har gjenopptatt filindeksering.
@@ -606,6 +624,7 @@ Comment[sr@latin]=Servis pretrage je nastavio indeksiranje fajlova.
 Comment[sv]=Filindexering har återupptagits av söktjänsten.
 Comment[th]=การทำดัชนีแฟ้มได้ดำเนินการต่อแล้วโดยบริการค้นหา
 Comment[tr]=Dosya indeksleyici arama servisi tarafından devam ettirildi.
+Comment[ug]=ھۆججەت ئىندېكسلاش ئىزدەش مۇلازىمىتى تەرىپىدىن داۋاملاشتۇرۇلدى.
 Comment[uk]=Індексування файлів було поновлено службою пошуку.
 Comment[x-test]=xxFile indexing has been resumed by the search service.xx
 Comment[zh_CN]=Strigi 文件索引操作已经被搜索服务恢复。
diff --git a/nepomuk/services/strigi/strigiservice.cpp b/nepomuk/services/strigi/strigiservice.cpp
index 9db5d56..0189679 100644
--- a/nepomuk/services/strigi/strigiservice.cpp
+++ b/nepomuk/services/strigi/strigiservice.cpp
@@ -38,14 +38,13 @@
 Nepomuk::StrigiService::StrigiService( QObject* parent, const QList<QVariant>& )
     : Service( parent )
 {
-    // store the little bits of ontology we need in Strigi
-    // (TODO: this needs to be deprecated in favor of something NIE)
-    // ==============================================================
-    Strigi::Util::storeStrigiMiniOntology( mainModel() );
-
     // setup the actual index scheduler including strigi stuff
     // ==============================================================
-    m_indexScheduler = new IndexScheduler( this );
+    m_schedulingThread = new QThread( this );
+    m_schedulingThread->start( QThread::IdlePriority );
+
+    m_indexScheduler = new IndexScheduler(); // must not have a parent
+    m_indexScheduler->moveToThread( m_schedulingThread );
 
     // monitor all kinds of events
     ( void )new EventMonitor( m_indexScheduler, this );
@@ -64,6 +63,8 @@ Nepomuk::StrigiService::StrigiService( QObject* parent, const QList<QVariant>& )
              this, SIGNAL( statusStringChanged() ) );
     connect( m_indexScheduler, SIGNAL( indexingFolder(QString) ),
              this, SIGNAL( statusStringChanged() ) );
+    connect( m_indexScheduler, SIGNAL( indexingFile(QString) ),
+             this, SIGNAL( statusStringChanged() ) );
     connect( m_indexScheduler, SIGNAL( indexingSuspended(bool) ),
              this, SIGNAL( statusStringChanged() ) );
 
@@ -75,9 +76,6 @@ Nepomuk::StrigiService::StrigiService( QObject* parent, const QList<QVariant>& )
     // FIXME: do not use a random delay value but wait for KDE to be started completely (using the session manager)
     QTimer::singleShot( 2*60*1000, this, SLOT( finishInitialization() ) );
 
-    // start the actual indexing
-    m_indexScheduler->start();
-
     // Connect some signals used in the DBus interface
     connect( this, SIGNAL( statusStringChanged() ),
              this, SIGNAL( statusChanged() ) );
@@ -92,8 +90,10 @@ Nepomuk::StrigiService::StrigiService( QObject* parent, const QList<QVariant>& )
 
 Nepomuk::StrigiService::~StrigiService()
 {
-    m_indexScheduler->stop();
-    m_indexScheduler->wait();
+    m_schedulingThread->quit();
+    m_schedulingThread->wait();
+
+    delete m_indexScheduler;
 }
 
 
@@ -153,16 +153,22 @@ QString Nepomuk::StrigiService::userStatusString( bool simple ) const
 {
     bool indexing = m_indexScheduler->isIndexing();
     bool suspended = m_indexScheduler->isSuspended();
-    QString folder = m_indexScheduler->currentFolder();
 
     if ( suspended ) {
         return i18nc( "@info:status", "File indexer is suspended" );
     }
     else if ( indexing ) {
+        QString folder = m_indexScheduler->currentFolder();
+
         if ( folder.isEmpty() || simple )
             return i18nc( "@info:status", "Strigi is currently indexing files" );
-        else
-            return i18nc( "@info:status", "Strigi is currently indexing files in folder %1", folder );
+        else {
+            QString file = KUrl( m_indexScheduler->currentFile() ).fileName();
+            if( file.isEmpty() )
+                return i18nc( "@info:status", "Strigi is currently indexing files in folder %1", folder );
+            else
+                return i18nc( "@info:status", "Strigi is currently indexing files in folder %1 (%2)", folder, file );
+        }
     }
     else {
         return i18nc( "@info:status", "File indexer is idle" );
@@ -270,28 +276,6 @@ void Nepomuk::StrigiService::indexFolder(const QString& path, bool recursive, bo
 }
 
 
-void Nepomuk::StrigiService::analyzeResource(const QString& uri, uint mTime, const QByteArray& data)
-{
-    QDataStream stream( data );
-    m_indexScheduler->analyzeResource( QUrl::fromEncoded( uri.toAscii() ), QDateTime::fromTime_t( mTime ), stream );
-}
-
-
-void Nepomuk::StrigiService::analyzeResourceFromTempFileAndDeleteTempFile(const QString& uri, uint mTime, const QString& tmpFile)
-{
-    QFile file( tmpFile );
-    if ( file.open( QIODevice::ReadOnly ) ) {
-        QDataStream stream( &file );
-        m_indexScheduler->analyzeResource( QUrl::fromEncoded( uri.toAscii() ), QDateTime::fromTime_t( mTime ), stream );
-        file.remove();
-    }
-    else {
-        kDebug() << "Failed to open" << tmpFile;
-    }
-}
-
-
-
 
 #include <kpluginfactory.h>
 #include <kpluginloader.h>
diff --git a/nepomuk/services/strigi/strigiservice.h b/nepomuk/services/strigi/strigiservice.h
index ecff7a2..2eb7eac 100644
--- a/nepomuk/services/strigi/strigiservice.h
+++ b/nepomuk/services/strigi/strigiservice.h
@@ -22,7 +22,7 @@
 
 #include <Nepomuk/Service>
 #include <QtCore/QTimer>
-
+#include <QtCore/QThread>
 
 namespace Strigi {
     class IndexManager;
@@ -44,9 +44,6 @@ namespace Nepomuk {
         StrigiService( QObject* parent = 0, const QList<QVariant>& args = QList<QVariant>() );
         ~StrigiService();
 
-        //vHanda: Is this really required? I've removed all the code that uses it.
-        IndexScheduler* indexScheduler() const { return m_indexScheduler; }
-
     Q_SIGNALS:
         void statusStringChanged();
         void statusChanged(); //vHanda: Can't we just use statusStringChanged? or should that be renamed
@@ -86,7 +83,7 @@ namespace Nepomuk {
         void updateAllFolders( bool forced );
 
         /**
-         * Index a folder independant of its configuration status.
+         * Index a folder independent of its configuration status.
          */
         void indexFolder( const QString& path, bool recursive, bool forced );
 
@@ -95,9 +92,6 @@ namespace Nepomuk {
          */
         void indexFile( const QString& path );
 
-        void analyzeResource( const QString& uri, uint mTime, const QByteArray& data );
-        void analyzeResourceFromTempFileAndDeleteTempFile( const QString& uri, uint mTime, const QString& tmpFile );
-
     private Q_SLOTS:
         void finishInitialization();
         void updateWatches();
@@ -109,6 +103,7 @@ namespace Nepomuk {
         QString userStatusString( bool simple ) const;
 
         IndexScheduler* m_indexScheduler;
+        QThread* m_schedulingThread;
     };
 }
 
diff --git a/nepomuk/services/strigi/strigiserviceconfig.cpp b/nepomuk/services/strigi/strigiserviceconfig.cpp
index f1e7b72..4a71cf9 100644
--- a/nepomuk/services/strigi/strigiserviceconfig.cpp
+++ b/nepomuk/services/strigi/strigiserviceconfig.cpp
@@ -17,7 +17,6 @@
 */
 
 #include "strigiserviceconfig.h"
-#include "removablestorageserviceinterface.h"
 #include "fileexcludefilters.h"
 
 #include <QtCore/QStringList>
@@ -174,13 +173,16 @@ bool Nepomuk::StrigiServiceConfig::shouldFolderBeIndexed( const QString& path )
 bool Nepomuk::StrigiServiceConfig::shouldFileBeIndexed( const QString& fileName ) const
 {
     // check the filters
+    QMutexLocker lock( &m_folderCacheMutex );
     return !m_excludeFilterRegExpCache.exactMatch( fileName );
 }
 
 
 bool Nepomuk::StrigiServiceConfig::folderInFolderList( const QString& path, bool& exact ) const
 {
-    QString p = KUrl( path ).path( KUrl::RemoveTrailingSlash );
+    QMutexLocker lock( &m_folderCacheMutex );
+
+    const QString p = KUrl( path ).path( KUrl::RemoveTrailingSlash );
 
     // we traverse the list backwards to catch all exclude folders
     int i = m_folderCache.count();
@@ -249,13 +251,10 @@ namespace {
 
 void Nepomuk::StrigiServiceConfig::buildFolderCache()
 {
+    QMutexLocker lock( &m_folderCacheMutex );
+
     QStringList includeFoldersPlain = m_config.group( "General" ).readPathEntry( "folders", QStringList() << QDir::homePath() );
-    org::kde::nepomuk::RemovableStorage removableStorageService( "org.kde.nepomuk.services.removablestorageservice",
-                                                                 "/removablestorageservice",
-                                                                 QDBusConnection::sessionBus() );
-    if ( removableStorageService.isValid() )
-        includeFoldersPlain << removableStorageService.currentlyMountedAndIndexed();
-    QStringList excludeFoldersPlain = m_config.group( "General" ).readPathEntry( "exclude folders", QStringList() );;
+    QStringList excludeFoldersPlain = m_config.group( "General" ).readPathEntry( "exclude folders", QStringList() );
 
     m_folderCache.clear();
     insertSortFolders( includeFoldersPlain, true, m_folderCache );
@@ -266,6 +265,7 @@ void Nepomuk::StrigiServiceConfig::buildFolderCache()
 
 void Nepomuk::StrigiServiceConfig::buildExcludeFilterRegExpCache()
 {
+    QMutexLocker lock( &m_folderCacheMutex );
     m_excludeFilterRegExpCache.rebuildCacheFromFilterList( excludeFilters() );
 }
 
diff --git a/nepomuk/services/strigi/strigiserviceconfig.h b/nepomuk/services/strigi/strigiserviceconfig.h
index 02bfc5f..03d827f 100644
--- a/nepomuk/services/strigi/strigiserviceconfig.h
+++ b/nepomuk/services/strigi/strigiserviceconfig.h
@@ -22,6 +22,7 @@
 #include <QtCore/QObject>
 #include <QtCore/QList>
 #include <QtCore/QRegExp>
+#include <QtCore/QMutex>
 
 #include <kconfig.h>
 #include <kio/global.h>
@@ -137,6 +138,8 @@ namespace Nepomuk {
         /// cache of regexp objects for all exclude filters
         /// to prevent regexp parsing over and over
         RegExpCache m_excludeFilterRegExpCache;
+
+        mutable QMutex m_folderCacheMutex;
     };
 }
 
diff --git a/nepomuk/services/strigi/util.cpp b/nepomuk/services/strigi/util.cpp
index 14cc4e6..3736c50 100644
--- a/nepomuk/services/strigi/util.cpp
+++ b/nepomuk/services/strigi/util.cpp
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2007-2009 Sebastian Trueg <trueg@kde.org>
+  Copyright (C) 2007-2011 Sebastian Trueg <trueg@kde.org>
 
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
@@ -18,125 +18,42 @@
 */
 
 #include "util.h"
-
-#include <strigi/variant.h>
-#include <strigi/fieldtypes.h>
+#include "datamanagement.h"
+#include "nepomuktools.h"
 
 #include <QtCore/QUrl>
 #include <QtCore/QFile>
 #include <QtCore/QFileInfo>
 #include <QtCore/QUuid>
+#include <QtCore/QScopedPointer>
 #include <QtCore/QDebug>
 
-#include <Soprano/Model>
-#include <Soprano/Statement>
-#include <Soprano/Vocabulary/RDF>
-#include <Soprano/Vocabulary/RDFS>
-#include <Soprano/Vocabulary/NRL>
-#include <Soprano/Vocabulary/XMLSchema>
-
+#include <KJob>
+#include <KDebug>
+#include <KGlobal>
+#include <KComponentData>
 
-#define STRIGI_NS "http://www.strigi.org/data#"
 
-QUrl Strigi::Util::fieldUri( const std::string& s )
+KJob* Nepomuk::clearIndexedData( const QUrl& url )
 {
-    QString qKey = QString::fromUtf8( s.c_str() );
-    QUrl url;
-
-    // very naive test for proper URI
-    if ( qKey.contains( ":/" ) ) {
-        url = qKey;
-    }
-    else {
-        url = STRIGI_NS + qKey;
-    }
-
-    // just to be sure
-    if ( url.isRelative() ) {
-        url.setScheme( "http" );
-    }
-
-    return url;
+    return clearIndexedData(QList<QUrl>() << url);
 }
 
-
-QUrl Strigi::Util::fileUrl( const std::string& filename )
+KJob* Nepomuk::clearIndexedData( const QList<QUrl>& urls )
 {
-    QUrl url = QUrl::fromLocalFile( QFileInfo( QString::fromUtf8( filename.c_str() ) ).absoluteFilePath() );
-    url.setScheme( "file" );
-    return url;
-}
+    if ( urls.isEmpty() )
+        return 0;
 
+    kDebug() << urls;
 
-std::string Strigi::Util::fieldName( const QUrl& uri )
-{
-    QString s = uri.toString();
-    if ( s.startsWith( STRIGI_NS ) ) {
-        s = s.mid( strlen( STRIGI_NS ) );
+    //
+    // New way of storing Strigi Data
+    // The Datamanagement API will automatically find the resource corresponding to that url
+    //
+    KComponentData component = KGlobal::mainComponent();
+    if( component.componentName() != QLatin1String("nepomukindexer") ) {
+        component = KComponentData( QByteArray("nepomukindexer"),
+                                    QByteArray(), KComponentData::SkipMainComponentRegistration );
     }
-    return s.toUtf8().data();
-}
-
-
-QUrl Strigi::Util::uniqueUri( const QString& ns, Soprano::Model* model )
-{
-    QUrl uri;
-    do {
-        QString uid = QUuid::createUuid().toString();
-        uri = ( ns + uid.mid( 1, uid.length()-2 ) );
-    } while ( model->containsAnyStatement( Soprano::Statement( uri, Soprano::Node(), Soprano::Node() ) ) );
-    return uri;
-}
-
-
-Strigi::Variant Strigi::Util::nodeToVariant( const Soprano::Node& node )
-{
-    if ( node.isLiteral() ) {
-        switch( node.literal().type() ) {
-        case QVariant::Int:
-        case QVariant::UInt:
-        case QVariant::LongLong:  // FIXME: no perfect conversion :(
-        case QVariant::ULongLong:
-            return Strigi::Variant( node.literal().toInt() );
-
-        case QVariant::Bool:
-            return Strigi::Variant( node.literal().toBool() );
-
-        default:
-            return Strigi::Variant( node.literal().toString().toUtf8().data() );
-        }
-    }
-    else {
-        qWarning() << "(Soprano::Util::nodeToVariant) cannot convert non-literal node to variant.";
-        return Strigi::Variant();
-    }
-}
-
-
-void Strigi::Util::storeStrigiMiniOntology( Soprano::Model* model )
-{
-    // we use some nice URI here although we still have the STRIGI_NS for backwards comp
-
-    QUrl graph( "http://nepomuk.kde.org/ontologies/2008/07/24/strigi/metadata" );
-    Soprano::Statement depthProp( fieldUri( FieldRegister::embeddepthFieldName ),
-                                  Soprano::Vocabulary::RDF::type(),
-                                  Soprano::Vocabulary::RDF::Property(),
-                                  graph );
-    Soprano::Statement metaDataType( graph,
-                                     Soprano::Vocabulary::RDF::type(),
-                                     Soprano::Vocabulary::NRL::Ontology(),
-                                     graph );
-
-    if ( !model->containsStatement( depthProp ) ) {
-        model->addStatement( depthProp );
-    }
-    if ( !model->containsStatement( metaDataType ) ) {
-        model->addStatement( metaDataType );
-    }
-}
-
-
-QUrl Strigi::Ontology::indexGraphFor()
-{
-    return QUrl::fromEncoded( "http://www.strigi.org/fields#indexGraphFor", QUrl::StrictMode );
+    return Nepomuk::removeDataByApplication( urls, RemoveSubResoures, component );
 }
diff --git a/nepomuk/services/strigi/util.h b/nepomuk/services/strigi/util.h
index 9a1c8f4..2f5d268 100644
--- a/nepomuk/services/strigi/util.h
+++ b/nepomuk/services/strigi/util.h
@@ -1,5 +1,5 @@
 /*
-   Copyright (C) 2007-2009 Sebastian Trueg <trueg@kde.org>
+   Copyright (C) 2007-2011 Sebastian Trueg <trueg@kde.org>
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -20,37 +20,13 @@
 #ifndef _STRIGI_NEPOMUK_UTIL_H_
 #define _STRIGI_NEPOMUK_UTIL_H_
 
-#include <string>
+#include <KUrl>
 
-class QUrl;
-class QString;
+class KJob;
 
-namespace Soprano {
-    class Model;
-    class Node;
+namespace Nepomuk {
+    /// remove all indexed data for \p url the datamanagement way
+    KJob* clearIndexedData( const QUrl& url );
+    KJob* clearIndexedData( const QList<QUrl>& urls );
 }
-
-namespace Strigi {
-
-    class Variant;
-
-    namespace Util {
-        QUrl fieldUri( const std::string& s );
-        QUrl fileUrl( const std::string& filename );
-        std::string fieldName( const QUrl& uri );
-        QUrl uniqueUri( const QString& ns, ::Soprano::Model* model );
-        Strigi::Variant nodeToVariant( const ::Soprano::Node& node );
-
-        /**
-         * For now only stores the parentUrl property so it can be
-         * searched.
-         */
-        void storeStrigiMiniOntology( ::Soprano::Model* model );
-    }
-
-    namespace Ontology {
-        QUrl indexGraphFor();
-    }
-}
-
 #endif
diff --git a/nepomuk/servicestub/CMakeLists.txt b/nepomuk/servicestub/CMakeLists.txt
index 0eab0ef..fb10963 100644
--- a/nepomuk/servicestub/CMakeLists.txt
+++ b/nepomuk/servicestub/CMakeLists.txt
@@ -30,6 +30,7 @@ if (Q_WS_MAC)
     set_target_properties(nepomukservicestub PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Nepomuk Service Stub")
 endif (Q_WS_MAC)
 
+
 target_link_libraries(nepomukservicestub
   ${QT_QTCORE_LIBRARY}
   ${QT_QTDBUS_LIBRARY}
diff --git a/nepomuk/servicestub/priority.cpp b/nepomuk/servicestub/priority.cpp
index 1c4e5687..4b140d8 100644
--- a/nepomuk/servicestub/priority.cpp
+++ b/nepomuk/servicestub/priority.cpp
@@ -91,14 +91,13 @@ bool lowerPriority()
 }
 
 
-// FIXME: is this really useful? Should we better use SCHED_IDLE?
 bool lowerSchedulingPriority()
 {
-#ifdef SCHED_BATCH
+#ifdef SCHED_IDLE
     struct sched_param param;
     memset( &param, 0, sizeof(param) );
     param.sched_priority = 0;
-    return !sched_setscheduler( 0, SCHED_BATCH, &param );
+    return !sched_setscheduler( 0, SCHED_IDLE, &param );
 #else
     return false;
 #endif