Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F16570998
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
34 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/nepomuk/query/queryparser.cpp b/nepomuk/query/queryparser.cpp
index 3bdcb257c8..fe215d3094 100644
--- a/nepomuk/query/queryparser.cpp
+++ b/nepomuk/query/queryparser.cpp
@@ -1,755 +1,782 @@
/*
This file is part of the Nepomuk KDE project.
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 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 "queryparser.h"
#include "query.h"
#include "query_p.h"
#include "literalterm.h"
#include "resourceterm.h"
#include "andterm.h"
#include "orterm.h"
#include "negationterm.h"
#include "comparisonterm.h"
#include "dateparser_p.h"
#include "nfo.h"
#include <QtCore/QRegExp>
#include <QtCore/QSet>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <kdebug.h>
#include <klocale.h>
#include "resourcemanager.h"
#include "property.h"
#include "literal.h"
#include <Soprano/Node>
#include <Soprano/Model>
#include <Soprano/QueryResultIterator>
#include <Soprano/Vocabulary/RDFS>
#include <Soprano/Vocabulary/RDF>
using namespace Nepomuk::Query;
namespace {
Nepomuk::Query::ComparisonTerm::Comparator fieldTypeRelationFromString( const QString& s ) {
if ( s == "=" ) {
return Nepomuk::Query::ComparisonTerm::Equal;
}
else if ( s == ":" ) {
return Nepomuk::Query::ComparisonTerm::Contains;
}
else if ( s == ">" ) {
return Nepomuk::Query::ComparisonTerm::Greater;
}
else if ( s == "<" ) {
return Nepomuk::Query::ComparisonTerm::Smaller;
}
else if ( s == ">=" ) {
return Nepomuk::Query::ComparisonTerm::GreaterOrEqual;
}
else if ( s == "<=" ) {
return Nepomuk::Query::ComparisonTerm::SmallerOrEqual;
}
else {
kDebug() << "FIXME: Unsupported relation:" << s;
return Nepomuk::Query::ComparisonTerm::Equal;
}
}
QString stripQuotes( const QString& s, bool* hadQuotes = 0 ) {
if ( s[0] == '\'' ||
s[0] == '\"' ) {
if( hadQuotes )
*hadQuotes = true;
return s.mid( 1 ).left( s.length()-2 );
}
else {
if( hadQuotes )
*hadQuotes = false;
return s;
}
}
QUrl tryToBeIntelligentAboutParsingUrl( const QString& s ) {
if ( s.contains( '%' ) && !s.contains( '/' ) ) {
return QUrl::fromEncoded( s.toAscii() );
}
else {
return QUrl( s );
}
}
Soprano::LiteralValue createLiteral( const QString& s, bool globbing ) {
// 1. check if it is a number
QString clearString(s);
clearString.remove(QLatin1Char('\''));
clearString.remove(QLatin1Char('"'));
bool b = false;
int i = clearString.toInt( &b );
if ( b )
return Soprano::LiteralValue( i );
double d = clearString.toDouble( &b );
if ( b )
return Soprano::LiteralValue( d );
// 2. no number - continue with the original string
// no globbing if we have quotes or if there already is a wildcard
if ( s[0] == QLatin1Char('\'') ||
s[0] == QLatin1Char('\"') ) {
return s;
}
//
// we can only do query term globbing for strings longer than 3 chars
//
if( globbing && s.length() > 3 && !s.endsWith('*') && !s.endsWith('?') )
return QString(s + '*');
else
return s;
}
bool positiveTerm( const QString& s ) {
if(s.isEmpty())
return true;
else if(s == "+")
return true;
else if(s == "-")
return false;
else //unrecognized capture
return true;
}
- // TODO do this dorectly while parsing (but only once we have a real parser based on bison or whatever
- Nepomuk::Query::Term resolveFields( const Nepomuk::Query::Term& term, const Nepomuk::Query::QueryParser* parser )
- {
- switch( term.type() ) {
- case Nepomuk::Query::Term::And:
- case Nepomuk::Query::Term::Or: {
- QList<Nepomuk::Query::Term> newSubTerms;
- foreach( const Nepomuk::Query::Term& t, static_cast<const Nepomuk::Query::GroupTerm&>( term ).subTerms() ) {
- Nepomuk::Query::Term resolvedTerm = resolveFields(t, parser);
- if ( resolvedTerm.isValid() )
- newSubTerms << resolvedTerm;
- else
- return Nepomuk::Query::Term();
- }
- if ( term.isAndTerm() )
- return Nepomuk::Query::AndTerm( newSubTerms );
- else
- return Nepomuk::Query::OrTerm( newSubTerms );
- }
-
-
- case Nepomuk::Query::Term::Negation: {
- return Nepomuk::Query::NegationTerm::negateTerm( resolveFields( term.toNegationTerm().subTerm(), parser ) );
- }
-
-
- case Nepomuk::Query::Term::Comparison: {
- Nepomuk::Query::ComparisonTerm newTerm;
- newTerm.setComparator( term.toComparisonTerm().comparator() );
- newTerm.setProperty( term.toComparisonTerm().property() );
- newTerm.setSubTerm( resolveFields( term.toComparisonTerm().subTerm(), parser ) );
-
- // needLiteralRange states if we need a literal range. A value of false does not mean that we need a resource though.
- bool needLiteralRange = newTerm.subTerm().isLiteralTerm() && !newTerm.subTerm().toLiteralTerm().value().isString();
-
- // A very dumb test to see if the property is set or not: does the URI have a scheme.
- // With a proper parser and in-place property matching there will be no need for this anymore
- if ( newTerm.property().uri().scheme().isEmpty() ) {
- QList<Nepomuk::Types::Property> properties = parser->matchProperty( term.toComparisonTerm().property().uri().toString() );
- if ( properties.count() > 0 ) {
- if ( properties.count() == 1 ) {
- newTerm.setProperty( properties.first() );
- if(needLiteralRange) {
- QVariant v = newTerm.subTerm().toLiteralTerm().value().variant();
- v.convert(properties[0].literalRangeType().dataType());
- newTerm.setSubTerm(Nepomuk::Query::LiteralTerm(v));
- if(newTerm.comparator() == Nepomuk::Query::ComparisonTerm::Contains && v.type() != QVariant::String) {
- newTerm.setComparator(Nepomuk::Query::ComparisonTerm::Equal);
- }
- }
- return newTerm;
- }
- else {
- // we only use a max of 4 properties, otherwise the queries get too big
- // in addition we try to exclude properties which do not make sense:
- // - numerical values can never match a resource
- Nepomuk::Query::OrTerm orTerm;
- for( int i = 0; i < properties.count() && orTerm.subTerms().count() < 4; ++i ) {
- const Nepomuk::Types::Property& property = properties[i];
- if(!property.range().isValid() || !needLiteralRange) {
- QVariant v = newTerm.subTerm().toLiteralTerm().value().variant();
- if(property.range().isValid() ||
- v.convert(property.literalRangeType().dataType())) {
- Nepomuk::Query::ComparisonTerm t( newTerm );
- t.setProperty( property );
- t.setSubTerm(Nepomuk::Query::LiteralTerm(v));
- // only strings can be matched via bif:contains
- if(t.comparator() == Nepomuk::Query::ComparisonTerm::Contains && v.type() != QVariant::String) {
- t.setComparator(Nepomuk::Query::ComparisonTerm::Equal);
- }
- orTerm.addSubTerm( t );
- }
- }
- }
- if(!orTerm.isValid()) {
- // FIXME: actually the whole query might be invalid at this point.
- return Nepomuk::Query::Term();
- }
- return orTerm;
- }
- }
- else {
- return Nepomuk::Query::Term();
- }
- }
- }
-
- default:
- return term;
- }
- }
-
// A filename pattern needs to contain one dot and at least one '*' or '?':
// *.mp3
// hello?.txt
// hello?.*
// test*.???
bool isFilenamePattern( const QString& s )
{
return( !s.contains(' ') &&
s.count('.') == 1 &&
s.count('*') + s.count('?') > 0 );
}
Nepomuk::Query::ComparisonTerm createFilenamePatternTerm( const QString& s )
{
QString regex = QRegExp::escape(s);
regex.replace( "\\*", QLatin1String( ".*" ) );
regex.replace( "\\?", QLatin1String( "." ) );
regex.replace("\\", "\\\\");
regex.prepend('^');
regex.append('$');
return Nepomuk::Query::ComparisonTerm( Nepomuk::Vocabulary::NFO::fileName(),
Nepomuk::Query::LiteralTerm( regex ),
Nepomuk::Query::ComparisonTerm::Regexp );
}
/**
* Merging literal terms is an optimization which is based on the assumption that most
* users want to search for the full text terms they enter in the value of the same
* property.
* Since merging two literals "foo" and "bar" into one term "foo AND bar" effectively
* changes the result set (the former allows that "foo" occurs in a property value
* different from "bar" while the latter forces them to occur in the same.)
* But the resulting query is much faster.
*/
Nepomuk::Query::Term mergeLiteralTerms( const Nepomuk::Query::Term& term )
{
if( term.isAndTerm() ) {
AndTerm mergedTerm;
QStringList fullTextTerms;
Q_FOREACH( const Term& st, term.toAndTerm().subTerms() ) {
if( st.isLiteralTerm() ) {
fullTextTerms << st.toLiteralTerm().value().toString();
}
else {
mergedTerm.addSubTerm( st );
}
}
mergedTerm.addSubTerm( LiteralTerm( fullTextTerms.join( QString::fromLatin1(" AND ") ) ) );
return mergedTerm.optimized();
}
else {
return term;
}
}
+ /**
+ * Tries to setup the sub-term of the given comparisonterm according to its property.
+ * This can fail if the sub-term cannot be converted to the property's range.
+ *
+ * This is used in QueryParser::Private::resolveFields
+ */
+ bool setupComparisonTermSubTerm(Nepomuk::Query::ComparisonTerm& ct)
+ {
+ // property with resource range
+ if(ct.property().range().isValid()) {
+ if(ct.subTerm().isLiteralTerm()) {
+ // here we need a string. everything else does not work since we match labels
+ return ct.subTerm().toLiteralTerm().value().isString();
+ }
+ else {
+ return true;
+ }
+ }
+
+ // property with literal range
+ else if(ct.subTerm().isLiteralTerm()) {
+ QVariant v = ct.subTerm().toLiteralTerm().value().variant();
+ if(v.convert(ct.property().literalRangeType().dataType())) {
+ ct.setSubTerm(Nepomuk::Query::LiteralTerm(v));
+ // only strings can be matched via bif:contains
+ if(ct.comparator() == Nepomuk::Query::ComparisonTerm::Contains && v.type() != QVariant::String) {
+ ct.setComparator(Nepomuk::Query::ComparisonTerm::Equal);
+ }
+ return true;
+ }
+ }
+
+ // fallback
+ return false;
+ }
+
#ifndef Q_CC_MSVC
#warning Make the parser handle different data, time, and datetime encodings as well as suffixes like MB or GB
#endif
#if 0
QDateTime parseDateTime( const Soprano::LiteralValue& literal )
{
//TODO: change to DateTime parser once complete
Nepomuk::Search::DateParser date( literal.toString() );
if( date.hasDate() ) {
return QDateTime( date.getDate() );
}
else {
Nepomuk::Search::TimeParser time( literal.toString() );
if(time.hasTime() )
return QDateTime(QDate::currentDate(), time.next() );
else
return QDateTime(); //return invalid datetime
}
}
Soprano::LiteralValue parseSizeType( const Soprano::LiteralValue& literal )
{
const double KiB = 1024.0;
const double MiB = KiB * 1024.0;
const double GiB = MiB * 1024.0;
const double TiB = GiB * 1024.0;
const double KB = 1000.0;
const double MB = KB * 1000.0;
const double GB = MB * 1000.0;
const double TB = GB * 1000.0;
QHash<QString, double> sizes;
sizes.insert( "KiB", KiB );
sizes.insert( "MiB", MiB );
sizes.insert( "GiB", GiB );
sizes.insert( "TiB", TiB );
sizes.insert( "KB", KB );
sizes.insert( "MB", MB );
sizes.insert( "GB", GB );
sizes.insert ("TB", TB );
for ( QHash<QString, double>::const_iterator i = sizes.constBegin();
i != sizes.constEnd(); ++i ) {
QRegExp cur( QString("^([\\d]+.?[\\d]*)[\\s]*%1$").arg( i.key() ) );
if( cur.indexIn( literal.toString() ) != -1 ) {
double value = cur.cap( 1 ).toDouble();
double newValue = value * i.value();
kDebug() << "Found value" << value << i.key() << "->" << newValue;
return Soprano::LiteralValue( newValue );
}
}
return literal;
}
Nepomuk::Query::Term resolveLiteralValues( const Nepomuk::Query::Term& term )
{
switch( term.type() ) {
case Nepomuk::Query::Term::Comparison: {
Nepomuk::Query::ComparisonTerm cterm = term.toComparisonTerm();
Nepomuk::Types::Property p( cterm.property() );
if ( p.literalRangeType().isValid() ) {
Q_ASSERT( cterm.subTerm().isLiteralTerm() );
Nepomuk::Query::ComparisonTerm newTerm;
newTerm.setComparator( cterm.comparator() );
newTerm.setProperty( QUrl(cterm.property()) );
// now try to resolve the literal
const Nepomuk::Query::LiteralTerm subTerm = cterm.subTerm().toLiteralTerm();
const Nepomuk::Types::Literal lt = p.literalRangeType();
if ( lt.dataType() == QVariant::DateTime &&
!subTerm.value().isDateTime() ) {
QDateTime dateTime = parseDateTime( subTerm.value() );
if ( dateTime.isValid() ) {
newTerm.setSubTerm( Nepomuk::Query::LiteralTerm( dateTime ) );
return newTerm;
}
}
else if ( lt.dataType() == QVariant::Int &&
!subTerm.value().isInt() ) {
newTerm.setSubTerm( Nepomuk::Query::LiteralTerm( parseSizeType( subTerm.value() ) ) );
return newTerm;
}
}
return term;
}
case Nepomuk::Query::Term::And:
case Nepomuk::Query::Term::Or: {
QList<Nepomuk::Query::Term> newSubTerms;
foreach( const Nepomuk::Query::Term& t, static_cast<const Nepomuk::Query::GroupTerm&>( term ).subTerms() ) {
newSubTerms << resolveLiteralValues(t);
}
if ( term.isAndTerm() )
return Nepomuk::Query::AndTerm( newSubTerms );
else
return Nepomuk::Query::OrTerm( newSubTerms );
}
default:
return term;
}
}
#endif
// a field differs from a plain term in that it does never allow comparators
const char* s_fieldNamePattern = "([^\\s\"':=<>]+|(?:([\"'])[^\"':=<>]+\\%1))";
const char* s_plainTermPattern = "([^\\s\"':=<>]+|(?:([\"'])[^\"']+\\%1))";
const char* s_inExclusionPattern = "([\\+\\-]?)";
const char* s_uriPattern = "<([^<>]+)>";
const char* s_comparatorPattern = "(:|\\<=|\\>=|=|\\<|\\>)";
/**
* Creating QRegExp is expensive, copying them is cheap. Thus, we keep static
* instances of the regexps around which we only have to create once.
*/
class QueryParserRegExpPool
{
public:
QueryParserRegExpPool()
: plainTermRx( QLatin1String(s_inExclusionPattern)
+ QString::fromLatin1(s_plainTermPattern).arg( 3 ) ),
fieldRx( QLatin1String(s_inExclusionPattern)
+ QString::fromLatin1(s_fieldNamePattern).arg( 3 )
+ QLatin1String(s_comparatorPattern)
+ QString::fromLatin1(s_plainTermPattern).arg( 6 ) ),
propertyRx( QLatin1String(s_inExclusionPattern)
+ QLatin1String(s_uriPattern)
+ QLatin1String(s_comparatorPattern)
+ QString::fromLatin1(s_plainTermPattern).arg( 5 ) ),
resourceRx( QLatin1String(s_inExclusionPattern)
+ QLatin1String(s_uriPattern)
+ QLatin1String("(?::|=)")
+ QLatin1String(s_uriPattern) ),
fieldFieldRx( QLatin1String(s_inExclusionPattern)
+ QString::fromLatin1(s_fieldNamePattern).arg( 3 )
+ QLatin1String(s_comparatorPattern)
+ QLatin1String("\\(")
+ QString::fromLatin1(s_fieldNamePattern).arg( 6 )
+ QLatin1String(s_comparatorPattern)
+ QString::fromLatin1(s_plainTermPattern).arg( 9 )
+ QLatin1String("\\)") )
{
}
// match a simple search text
// captures: 1 - The optional + or - sign (may be empty)
// 2 - the search text (including optional paranthesis)
QRegExp plainTermRx;
// match a field search term: fieldname + relation (:, =, etc) + search text with optional paranthesis
// captures: 1 - The optional + or - sign (may be empty)
// 2 - fieldname
// 3 - relation
// 4 - search text (including optional paranthesis)
QRegExp fieldRx;
// match a property URI search term: property URI + relation (:, =, etc) + search text with optional paranthesis
// captures: 1 - The optional + or - sign (may be empty)
// 2 - property URI
// 3 - relation
// 4 - search text (including optional paranthesis)
QRegExp propertyRx;
// match a property URI search term: property URI + relation (:, =, etc) + resource URI
// captures: 1 - The optional + or - sign (may be empty)
// 2 - property URI
// 3 - resource URI
QRegExp resourceRx;
QRegExp fieldFieldRx;
};
// the one global instance used for the statis QueryParser methods
K_GLOBAL_STATIC( QueryParserRegExpPool, s_regExpPool )
}
class Nepomuk::Query::QueryParser::Private
{
public:
+ QueryParser* q;
+
QSet<QString> andKeywords;
QSet<QString> orKeywords;
mutable QHash<QString, QList<Types::Property> > fieldMatchCache;
QMutex fieldMatchCacheMutex;
+
+ /// set by resolveFields if the query is in fact invalid
+ bool m_invalidQuery;
+
+ /**
+ * Resolve the fields in all ComparisonTerms by looking up possible matching
+ * properties.
+ * This method will set m_invalidQuery in case there is one term for which
+ * no property can be matched.
+ */
+ Nepomuk::Query::Term resolveFields( const Nepomuk::Query::Term& term );
};
+
+Term QueryParser::Private::resolveFields(const Term &term)
+{
+ switch( term.type() ) {
+ case Nepomuk::Query::Term::And:
+ case Nepomuk::Query::Term::Or: {
+ QList<Nepomuk::Query::Term> newSubTerms;
+ foreach( const Nepomuk::Query::Term& t, static_cast<const Nepomuk::Query::GroupTerm&>( term ).subTerms() ) {
+ Nepomuk::Query::Term resolvedTerm = resolveFields(t);
+ if ( resolvedTerm.isValid() )
+ newSubTerms << resolvedTerm;
+ else
+ return Nepomuk::Query::Term();
+ }
+ if ( term.isAndTerm() )
+ return Nepomuk::Query::AndTerm( newSubTerms );
+ else
+ return Nepomuk::Query::OrTerm( newSubTerms );
+ }
+
+
+ case Nepomuk::Query::Term::Negation: {
+ return Nepomuk::Query::NegationTerm::negateTerm( resolveFields( term.toNegationTerm().subTerm() ) );
+ }
+
+
+ case Nepomuk::Query::Term::Comparison: {
+ Nepomuk::Query::ComparisonTerm newTerm;
+ newTerm.setComparator( term.toComparisonTerm().comparator() );
+ newTerm.setProperty( term.toComparisonTerm().property() );
+ newTerm.setSubTerm( resolveFields( term.toComparisonTerm().subTerm() ) );
+
+ // A very dumb test to see if the property is set or not: does the URI have a scheme.
+ // With a proper parser and in-place property matching there will be no need for this anymore
+ if ( newTerm.property().uri().scheme().isEmpty() ) {
+ QList<Nepomuk::Types::Property> properties = q->matchProperty( term.toComparisonTerm().property().uri().toString() );
+ // we only use a max of 4 properties, otherwise the queries get too big
+ // in addition we try to exclude properties which do not make sense:
+ // - numerical values can never match a resource
+ Nepomuk::Query::OrTerm orTerm;
+ for( int i = 0; i < properties.count() && orTerm.subTerms().count() < 4; ++i ) {
+ const Nepomuk::Types::Property& property = properties[i];
+ Nepomuk::Query::ComparisonTerm t( newTerm );
+ t.setProperty( property );
+ if(setupComparisonTermSubTerm(t)) {
+ orTerm.addSubTerm( t );
+ }
+ }
+ if(!orTerm.isValid()) {
+ m_invalidQuery = true;
+ return Nepomuk::Query::Term();
+ }
+ return orTerm.optimized();
+ }
+ }
+
+ default:
+ return term;
+ }
+}
+
+
Nepomuk::Query::QueryParser::QueryParser()
: d( new Private() )
{
+ d->q = this;
+
QString andListStr = i18nc( "Boolean AND keyword in desktop search strings. "
"You can add several variants separated by spaces, "
"e.g. retain the English one alongside the translation; "
"keywords are not case sensitive. Make sure there is "
"no conflict with the OR keyword.",
"and" );
foreach ( const QString &andKeyword, andListStr.split( ' ', QString::SkipEmptyParts ) ) {
d->andKeywords.insert( andKeyword.toLower() );
}
QString orListStr = i18nc( "Boolean OR keyword in desktop search strings. "
"You can add several variants separated by spaces, "
"e.g. retain the English one alongside the translation; "
"keywords are not case sensitive. Make sure there is "
"no conflict with the AND keyword.",
"or" );
foreach ( const QString &orKeyword, orListStr.split( ' ', QString::SkipEmptyParts ) ) {
d->orKeywords.insert( orKeyword.toLower() );
}
}
Nepomuk::Query::QueryParser::~QueryParser()
{
delete d;
}
QList<Nepomuk::Types::Property> Nepomuk::Query::QueryParser::matchProperty( const QString& fieldName ) const
{
kDebug() << fieldName;
QMutexLocker lock( &d->fieldMatchCacheMutex );
QHash<QString, QList<Types::Property> >::ConstIterator it = d->fieldMatchCache.constFind( fieldName );
if( it != d->fieldMatchCache.constEnd() ) {
return it.value();
}
else {
lock.unlock();
QList<Nepomuk::Types::Property> results;
//
// Due to the limited number of properties in the database a REGEX filter
// is actually faster than a fulltext query via bif:contains (this is what
// experiments showed).
//
QString query = QString( "select distinct ?p where { "
"graph ?g { "
"?p a %1 . "
"?p %2 ?l . "
"FILTER(REGEX(STR(?l),'%3*','i') || REGEX(STR(?p),'%3*','i')) . "
"} "
"}" )
.arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::RDF::Property() ) )
.arg( Soprano::Node::resourceToN3( Soprano::Vocabulary::RDFS::label() ) )
.arg( fieldName );
kDebug() << "Match query:" << query;
Soprano::QueryResultIterator labelHits
= Nepomuk::ResourceManager::instance()->mainModel()->executeQuery( query, Soprano::Query::QueryLanguageSparql );
while ( labelHits.next() ) {
QUrl property = labelHits.binding( "p" ).uri();
results << property;
kDebug() << "Found property match" << property;
}
lock.relock();
d->fieldMatchCache.insert( fieldName, results );
return results;
}
}
Nepomuk::Query::Query Nepomuk::Query::QueryParser::parse( const QString& query ) const
{
return parse( query, NoParserFlags );
}
Nepomuk::Query::Query Nepomuk::Query::QueryParser::parse( const QString& query, ParserFlags flags ) const
{
// TODO: a "real" parser which can handle all of the Xesam user language
// This one for example does not handle nesting at all.
Nepomuk::Query::Query final;
QList<Term> terms;
bool inOrBlock = false;
bool inAndBlock = false;
int pos = 0;
// create local copies of the regexps for thread safety purposes
const QRegExp resourceRx = s_regExpPool->resourceRx;
const QRegExp propertyRx = s_regExpPool->propertyRx;
const QRegExp fieldFieldRx = s_regExpPool->fieldFieldRx;
const QRegExp fieldRx = s_regExpPool->fieldRx;
const QRegExp plainTermRx = s_regExpPool->plainTermRx;
while ( pos < query.length() ) {
// skip whitespace
while ( pos < query.length() && query[pos].isSpace() ) {
kDebug() << "Skipping space at" << pos;
++pos;
}
Term term;
if ( pos < query.length() ) {
if ( resourceRx.indexIn( query, pos ) == pos ) {
kDebug() << "matched resource term at" << pos << resourceRx.cap( 0 );
term = ComparisonTerm( tryToBeIntelligentAboutParsingUrl( resourceRx.cap( 2 ) ),
ResourceTerm( tryToBeIntelligentAboutParsingUrl( resourceRx.cap( 3 ) ) ),
ComparisonTerm::Equal );
if ( !positiveTerm( resourceRx.cap( 1 ) ) ) {
term = NegationTerm::negateTerm( term );
}
pos += resourceRx.matchedLength();
}
else if ( propertyRx.indexIn( query, pos ) == pos ) {
kDebug() << "matched property term at" << pos << propertyRx.cap( 0 );
ComparisonTerm ct;
ct.setProperty( tryToBeIntelligentAboutParsingUrl( propertyRx.cap( 2 ) ) );
ct.setSubTerm( LiteralTerm( createLiteral( propertyRx.cap( 4 ), flags&QueryTermGlobbing ) ) );
QString comparator = propertyRx.cap( 3 );
ct.setComparator( fieldTypeRelationFromString( comparator ) );
pos += propertyRx.matchedLength();
if ( !positiveTerm(propertyRx.cap( 1 ) ) ) {
term = NegationTerm::negateTerm( ct );
}
else {
term = ct;
}
}
else if ( fieldFieldRx.indexIn( query, pos ) == pos ) {
kDebug() << "matched field field term at" << pos
<< fieldFieldRx.cap( 0 )
<< fieldFieldRx.cap( 2 )
<< fieldFieldRx.cap( 4 )
<< fieldFieldRx.cap( 5 )
<< fieldFieldRx.cap( 7 )
<< fieldFieldRx.cap( 8 );
ComparisonTerm ct;
ct.setProperty( QUrl(stripQuotes( fieldFieldRx.cap( 2 ) )) );
QString comparator = fieldFieldRx.cap( 4 );
ct.setComparator( fieldTypeRelationFromString( comparator ) );
ct.setSubTerm( ComparisonTerm( QUrl(stripQuotes( fieldFieldRx.cap( 5 ) )),
LiteralTerm( createLiteral( fieldFieldRx.cap( 8 ), flags&QueryTermGlobbing ) ),
fieldTypeRelationFromString( fieldFieldRx.cap( 7 ) ) ) );
pos += fieldFieldRx.matchedLength();
if ( !positiveTerm( fieldFieldRx.cap( 1 ) ) ) {
term = NegationTerm::negateTerm( ct );
}
else {
term = ct;
}
}
else if ( fieldRx.indexIn( query, pos ) == pos ) {
kDebug() << "matched field term at" << pos << fieldRx.cap( 0 ) << fieldRx.cap( 2 ) << fieldRx.cap( 4 ) << fieldRx.cap( 5 );
if( stripQuotes ( fieldRx.cap( 2 ) ).compare( QString( "inFolder" ), Qt::CaseInsensitive ) == 0 ) {
KUrl url( fieldRx.cap( 5 ) );
kDebug() << "found include path" << url;
FileQuery fileQuery(final);
if ( positiveTerm( fieldRx.cap( 1 ) ) )
fileQuery.addIncludeFolder(url);
else
fileQuery.addExcludeFolder(url);
final = fileQuery;
pos += fieldRx.matchedLength();
}
else {
ComparisonTerm ct;
ct.setProperty( QUrl( stripQuotes( fieldRx.cap( 2 ) ) ) );
ct.setSubTerm( LiteralTerm( createLiteral( fieldRx.cap( 5 ), flags&QueryTermGlobbing ) ) );
QString comparator = fieldRx.cap( 4 );
ct.setComparator( fieldTypeRelationFromString( comparator ) );
pos += fieldRx.matchedLength();
if ( !positiveTerm(fieldRx.cap( 1 ) ) ) {
NegationTerm nt;
nt.setSubTerm( ct );
term = nt;
}
else {
term = ct;
}
}
}
else if ( plainTermRx.indexIn( query, pos ) == pos ) {
QString value = plainTermRx.cap( 2 );
if ( d->orKeywords.contains( value.toLower() ) ) {
inOrBlock = true;
}
else if ( d->andKeywords.contains( value.toLower() ) ) {
inAndBlock = true;
}
else {
kDebug() << "matched literal at" << pos << value;
if( flags&DetectFilenamePattern && isFilenamePattern(value) ) {
term = createFilenamePatternTerm( value );
}
else {
term = LiteralTerm( createLiteral( value, flags&QueryTermGlobbing ) );
}
if ( !positiveTerm(plainTermRx.cap( 1 ) ) ) {
term = NegationTerm::negateTerm( term );
}
}
pos += plainTermRx.matchedLength();
}
else {
kDebug() << "Invalid query at" << pos << query;
return Query();
}
if ( term.isValid() ) {
if ( inOrBlock && !terms.isEmpty() ) {
OrTerm orTerm;
orTerm.addSubTerm( terms.takeLast() );
orTerm.addSubTerm( term );
terms.append( orTerm );
}
else if ( inAndBlock && !terms.isEmpty() ) {
AndTerm andTerm;
andTerm.addSubTerm( terms.takeLast() );
andTerm.addSubTerm( term );
terms.append( andTerm );
}
else {
terms.append( term );
}
}
}
}
if ( terms.count() == 1 ) {
final.setTerm( terms[0] );
}
else if ( terms.count() > 0 ) {
AndTerm t;
t.setSubTerms( terms );
final.setTerm( t );
}
- final.setTerm( mergeLiteralTerms( resolveFields( final.term(), this ) ) );
- return final;
+ d->m_invalidQuery = false;
+ final.setTerm( mergeLiteralTerms( d->resolveFields( final.term() ) ) );
+ if(d->m_invalidQuery) {
+ return Query();
+ }
+ else {
+ return final;
+ }
}
// static
Nepomuk::Query::Query Nepomuk::Query::QueryParser::parseQuery( const QString& query )
{
QueryParser parser;
return parser.parse( query );
}
// static
Nepomuk::Query::Query Nepomuk::Query::QueryParser::parseQuery( const QString& query, ParserFlags flags )
{
QueryParser parser;
return parser.parse( query, flags );
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 1, 9:49 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10076160
Default Alt Text
(34 KB)
Attached To
Mode
rKL kdelibs
Attached
Detach File
Event Timeline
Log In to Comment