File 1b66b02e-kderuntime-nepomuk-47branch-partialurls.diff of Package kdebase4-runtime

commit 1b66b02ecf6a055159290849c8bf708a7043bd0d
Author: Sebastian Trueg <trueg@kde.org>
Date:   Thu Oct 20 21:32:14 2011 +0200

    Handle parent folders or convertable URLs.
    
    So far we converted URLs which actually exist. Now we also handle
    partial URLs like /media/foo if a storage is mounted on /media/foobar.
    This is handled by adding additional regex filters to the already
    existing one.
    
    BUG: 284529

diff --git a/nepomuk/common/removablemediacache.cpp b/nepomuk/common/removablemediacache.cpp
index dbb31c8..c6cde9b 100644
--- a/nepomuk/common/removablemediacache.cpp
+++ b/nepomuk/common/removablemediacache.cpp
@@ -165,6 +165,21 @@ const Nepomuk::RemovableMediaCache::Entry* Nepomuk::RemovableMediaCache::findEnt
 }
 
 
+QList<const Nepomuk::RemovableMediaCache::Entry*> Nepomuk::RemovableMediaCache::findEntriesByMountPath(const QString &path) const
+{
+    QList<const Entry*> entries;
+    for( QHash<QString, Entry>::const_iterator it = m_metadataCache.constBegin();
+        it != m_metadataCache.constEnd(); ++it ) {
+        const Entry& entry = *it;
+        if(entry.isMounted() &&
+           entry.mountPath().startsWith(path)) {
+            entries.append(&entry);
+        }
+    }
+    return entries;
+}
+
+
 bool Nepomuk::RemovableMediaCache::hasRemovableSchema(const KUrl &url) const
 {
     return m_usedSchemas.contains(url.scheme());
diff --git a/nepomuk/common/removablemediacache.h b/nepomuk/common/removablemediacache.h
index 25982f0..8060765 100644
--- a/nepomuk/common/removablemediacache.h
+++ b/nepomuk/common/removablemediacache.h
@@ -74,6 +74,14 @@ public:
     const Entry* findEntryByFilePath( const QString& path ) const;
     const Entry* findEntryByUrl(const KUrl& url) const;
 
+    /**
+     * Searches for entries which are mounted at a path which starts with
+     * the given one. Example: a \p path \p /media will result in all
+     * entries which are mounted under \p /media like \p /media/disk1 or
+     * \p /media/cdrom.
+     */
+    QList<const Entry*> findEntriesByMountPath(const QString& path) const;
+
     QList<const Entry*> allMedia() const;
 
     /**
diff --git a/nepomuk/services/storage/removablemediamodel.cpp b/nepomuk/services/storage/removablemediamodel.cpp
index 836668e..aa3c49c 100644
--- a/nepomuk/services/storage/removablemediamodel.cpp
+++ b/nepomuk/services/storage/removablemediamodel.cpp
@@ -193,14 +193,15 @@ Soprano::QueryResultIterator Nepomuk::RemovableMediaModel::executeQuery(const QS
     return new QueryResultIteratorBackend(this, FilterModel::executeQuery(convertFileUrls(query), language, userQueryLanguage));
 }
 
-Soprano::Node Nepomuk::RemovableMediaModel::convertFileUrl(const Soprano::Node &node) const
+Soprano::Node Nepomuk::RemovableMediaModel::convertFileUrl(const Soprano::Node &node, bool forRegEx) 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()) {
+                if(entry->isMounted() &&
+                   (!forRegEx || entry->mountPath().length() < localFilePath.length())) {
                     return entry->constructRelativeUrl(localFilePath);
                 }
             }
@@ -270,11 +271,28 @@ QString Nepomuk::RemovableMediaModel::convertFileUrls(const QString &query) cons
 
     // is 0, 1, or 3 - nothing else
     int quoteCnt = 0;
+
+    // if quoteCnt > 0, ie. we are in a literal this is the first char of it
     int literalStartPos = 0;
+
+    // true if we are in a regex filter
     bool inRegEx = false;
+
+    // if inRegEx is true this is the position where the regex starts in the new query
+    int newQueryRegExStart = 0;
+
+    // if inRegEx is true this is the variable used in the regex (including any functions)
+    QString variable;
+
+    // true if we are in a resource URI like: <....>
     bool inRes = false;
+
+    // the char used for quote ' or "
     QChar quote;
+
+    // the new query we construct
     QString newQuery;
+
     for(int i = 0; i < query.length(); ++i) {
         const QChar c = query[i];
 
@@ -359,7 +377,21 @@ QString Nepomuk::RemovableMediaModel::convertFileUrls(const QString &query) cons
                     query[i+2].toLower() == 'g' &&
                     query[i+3].toLower() == 'e' &&
                     query[i+4].toLower() == 'x') {
-                inRegEx = true;
+                // determine the variable
+                int pos = query.indexOf('(', i+4);
+                if(pos > i+4) {
+                    int endPos = query.indexOf(',', pos+1);
+                    if(endPos > pos+1) {
+                        inRegEx = true;
+                        variable = query.mid(pos+1, endPos-pos-1).trimmed();
+                        newQueryRegExStart = newQuery.length();
+                        // we already copy the entire start of the REGEX since our REGEX end check is way too simple
+                        // It would not handle function calls like REGEX(STR(?var)),...
+                        newQuery.append(query.mid(i, endPos-i+1));
+                        i = endPos;
+                        continue;
+                    }
+                }
             }
         }
 
@@ -401,10 +433,44 @@ QString Nepomuk::RemovableMediaModel::convertFileUrls(const QString &query) cons
                 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;
+                    KUrl convertedUrl = KUrl(convertFileUrl(fileUrl, true).uri());
+
+                    // 1. Case: we have an exact match with one of the removable media (this always includes a trailing slash)
+                    if(fileUrl != convertedUrl) {
+                        newQuery += convertedUrl.url();
+                        // set i to last char we handled and let the loop continue with the end of the quote
+                        i = pos-1;
+                        continue;
+                    }
+
+                    // 2. Case The query tries to match a super-folder of one of the removable media. In that case we need
+                    //    to add additional regex filters which match all candidates
+                    else {
+                        // create regex filters for all of them
+                        // We need to append a slash since we do not want to include a possibly unmounted medium "foobar" if only "foo" is mounted.
+                        QStringList filters;
+                        foreach(const RemovableMediaCache::Entry* entry, m_removableMediaCache->findEntriesByMountPath(fileUrl.toLocalFile())) {
+                            filters << QString::fromLatin1("REGEX(%1, '^%2/')").arg(variable, entry->constructRelativeUrl(QString()).url());
+                        }
+                        if(!filters.isEmpty()) {
+                            // 1. copy the original regex
+                            filters.prepend(QString::fromLatin1("REGEX(%1, '^%2')").arg(variable, query.mid(i, pos-i)));
+
+                            // 2. Strip away the previsouly copied REGEX term
+                            newQuery.truncate(newQueryRegExStart);
+
+                            // 3. append the new ones and add new parethesis
+                            newQuery.append('(');
+                            newQuery.append(filters.join(QLatin1String(" || ")));
+                            newQuery.append(')');
+
+                            // 4. update i:
+                            //    if we find a closing parenthesis, that is the last char we handled
+                            //    otherwise it is just pos+quoteCnt-1 since the last quote is what we handled
+                            i = qMax(pos+quoteCnt-1, query.indexOf(')', pos+1));
+                            continue;
+                        }
+                    }
                 }
             }
         }
diff --git a/nepomuk/services/storage/removablemediamodel.h b/nepomuk/services/storage/removablemediamodel.h
index 52e4eda..4926439 100644
--- a/nepomuk/services/storage/removablemediamodel.h
+++ b/nepomuk/services/storage/removablemediamodel.h
@@ -93,7 +93,13 @@ private:
      */
     Soprano::Statement convertFileUrls(const Soprano::Statement& s) const;
 
-    Soprano::Node convertFileUrl(const Soprano::Node& node) const;
+    /**
+     * Convert a local file URL into its internal counterpart or return the
+     * given URL if it does not need to be converted.
+     * \param forRegEx If true the base path of the storage medium will not
+     * be converted.
+     */
+    Soprano::Node convertFileUrl(const Soprano::Node& node, bool forRegEx = false) const;
 
     /**
      * Converts file:/ URLs into their filex:/ counterpart if necessary.
diff --git a/nepomuk/services/storage/test/removablemediamodeltest.cpp b/nepomuk/services/storage/test/removablemediamodeltest.cpp
index c47c26d..eeae40d 100644
--- a/nepomuk/services/storage/test/removablemediamodeltest.cpp
+++ b/nepomuk/services/storage/test/removablemediamodeltest.cpp
@@ -153,6 +153,10 @@ void RemovableMediaModelTest::testConvertFileUrlsInQuery_data()
             << 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("queryWithConvertableFileUrl1WeirdFormatting")
+            << 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> . }");
@@ -161,14 +165,48 @@ void RemovableMediaModelTest::testConvertFileUrlsInQuery_data()
             << 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("queryWithConvertableRegex1WithWeirdFormatting")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . filter(reGEx(  STR(?u)  ,  'file:///media/XO-Y4/test'  ) ) . }")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . filter(reGEx(  STR(?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/')) . }");
 
+    QTest::newRow("queryWithConvertableRegex3")
+            << 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/''')) . }");
+
+    // looking for anything in /media includes files from any storage mounted somewhere under /media
+    QTest::newRow("queryWithConvertableRegex4")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . FILTER(REGEX(?u, '^file:///media')) . }")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . "
+                                   "FILTER(("
+                                   "REGEX(?u, '^file:///media') || "
+                                   "REGEX(?u, '^optical://solidman_begins/') || "
+                                   "REGEX(?u, '^filex://whatever/') || "
+                                   "REGEX(?u, '^nfs://thehost/solid-path/') || "
+                                   "REGEX(?u, '^filex://xyz-123/'))"
+                                   ") . }");
+
+    QTest::newRow("queryWithConvertableRegex4WithWeirdFormatting")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . filter   (  reGEX(  str( ?u)  , '^file:///media'  ) ) . }")
+            << QString::fromLatin1("select ?r where { ?r nie:url ?u . "
+                                   "filter   (  ("
+                                   "REGEX(str( ?u), '^file:///media') || "
+                                   "REGEX(str( ?u), '^optical://solidman_begins/') || "
+                                   "REGEX(str( ?u), '^filex://whatever/') || "
+                                   "REGEX(str( ?u), '^nfs://thehost/solid-path/') || "
+                                   "REGEX(str( ?u), '^filex://xyz-123/'))"
+                                   " ) . }");
+
+    QTest::newRow("queryWithConvertableRegex4")
+            << 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, '^file:///media/nfs') || REGEX(?u, '^nfs://thehost/solid-path/'))) . }");
+
     QTest::newRow("queryWithNotReallyAFileUrl")
             << QString::fromLatin1("select ?r where { ?r ?p ?u . FILTER(REGEX(?u, 'ffile:///media/nfs/')) . }")
             << QString::fromLatin1("select ?r where { ?r ?p ?u . FILTER(REGEX(?u, 'ffile:///media/nfs/')) . }");
-    // TODO: add queries that should NOT be converted
 }
 
 void RemovableMediaModelTest::testConvertFileUrlsInQuery()
openSUSE Build Service is sponsored by