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()