mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-24 02:42:48 +00:00
493 lines
19 KiB
C++
493 lines
19 KiB
C++
/*
|
|
This file is part of the Nepomuk KDE project.
|
|
Copyright (C) 2008-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 "queryserializer.h"
|
|
|
|
#include "filequery.h"
|
|
#include "term.h"
|
|
#include "literalterm.h"
|
|
#include "resourceterm.h"
|
|
#include "andterm.h"
|
|
#include "orterm.h"
|
|
#include "negationterm.h"
|
|
#include "comparisonterm.h"
|
|
#include "comparisonterm_p.h"
|
|
#include "resourcetypeterm.h"
|
|
#include "optionalterm.h"
|
|
|
|
#include "property.h"
|
|
#include "class.h"
|
|
|
|
#include <Soprano/LiteralValue>
|
|
|
|
#include <kurl.h>
|
|
#include <kdebug.h>
|
|
|
|
#include <QtCore/QXmlStreamWriter>
|
|
|
|
|
|
using namespace Nepomuk::Query;
|
|
|
|
namespace {
|
|
|
|
QString aggregateToString( Nepomuk::Query::ComparisonTerm::AggregateFunction f )
|
|
{
|
|
switch( f ) {
|
|
case Nepomuk::Query::ComparisonTerm::Count:
|
|
return QString::fromLatin1("count");
|
|
case Nepomuk::Query::ComparisonTerm::DistinctCount:
|
|
return QString::fromLatin1("distinctcount");
|
|
case Nepomuk::Query::ComparisonTerm::Max:
|
|
return QString::fromLatin1("max");
|
|
case Nepomuk::Query::ComparisonTerm::Min:
|
|
return QString::fromLatin1("min");
|
|
case Nepomuk::Query::ComparisonTerm::Sum:
|
|
return QString::fromLatin1("sum");
|
|
case Nepomuk::Query::ComparisonTerm::DistinctSum:
|
|
return QString::fromLatin1("distinctsum");
|
|
case Nepomuk::Query::ComparisonTerm::Average:
|
|
return QString::fromLatin1("avg");
|
|
case Nepomuk::Query::ComparisonTerm::DistinctAverage:
|
|
return QString::fromLatin1("distinctavg");
|
|
default:
|
|
return QString();
|
|
}
|
|
}
|
|
|
|
Nepomuk::Query::ComparisonTerm::AggregateFunction stringToAggregate( const QStringRef& f )
|
|
{
|
|
if( f == QString::fromLatin1("count") )
|
|
return Nepomuk::Query::ComparisonTerm::Count;
|
|
else if( f == QString::fromLatin1("distinctcount") )
|
|
return Nepomuk::Query::ComparisonTerm::DistinctCount;
|
|
else if( f == QString::fromLatin1("max") )
|
|
return Nepomuk::Query::ComparisonTerm::Max;
|
|
else if( f == QString::fromLatin1("min") )
|
|
return Nepomuk::Query::ComparisonTerm::Min;
|
|
else if( f == QString::fromLatin1("sum") )
|
|
return Nepomuk::Query::ComparisonTerm::Sum;
|
|
else if( f == QString::fromLatin1("distinctsum") )
|
|
return Nepomuk::Query::ComparisonTerm::DistinctSum;
|
|
else if( f == QString::fromLatin1("avg") )
|
|
return Nepomuk::Query::ComparisonTerm::Average;
|
|
else if( f == QString::fromLatin1("distinctavg") )
|
|
return Nepomuk::Query::ComparisonTerm::DistinctAverage;
|
|
else
|
|
return Nepomuk::Query::ComparisonTerm::NoAggregateFunction;
|
|
}
|
|
|
|
|
|
bool doSerializeTerm( QXmlStreamWriter& xml, const Nepomuk::Query::Term& term )
|
|
{
|
|
switch(term.type()) {
|
|
case Term::Literal: {
|
|
xml.writeStartElement( QLatin1String("literal") );
|
|
const Soprano::LiteralValue value = term.toLiteralTerm().value();
|
|
if( value.isPlain() )
|
|
xml.writeAttribute( QLatin1String("lang"), value.language().toString() );
|
|
else
|
|
xml.writeAttribute( QLatin1String("datatype"), KUrl(value.dataTypeUri()).url() );
|
|
xml.writeCharacters( value.toString() );
|
|
xml.writeEndElement();
|
|
break;
|
|
}
|
|
|
|
case Term::Resource:
|
|
xml.writeStartElement( QLatin1String("resource") );
|
|
xml.writeAttribute( QLatin1String("uri"), KUrl( term.toResourceTerm().resource().resourceUri() ).url() );
|
|
xml.writeEndElement();
|
|
break;
|
|
|
|
case Term::And:
|
|
xml.writeStartElement( QLatin1String("and") );
|
|
Q_FOREACH( const Term& myterm, term.toAndTerm().subTerms() ) {
|
|
doSerializeTerm( xml, myterm );
|
|
}
|
|
xml.writeEndElement();
|
|
break;
|
|
|
|
case Term::Or:
|
|
xml.writeStartElement( QLatin1String("or") );
|
|
Q_FOREACH( const Term& myterm, term.toOrTerm().subTerms() ) {
|
|
doSerializeTerm( xml, myterm );
|
|
}
|
|
xml.writeEndElement();
|
|
break;
|
|
|
|
case Term::Comparison: {
|
|
ComparisonTerm cTerm( term.toComparisonTerm() );
|
|
|
|
xml.writeStartElement( QLatin1String("comparison") );
|
|
|
|
if( cTerm.property().isValid() )
|
|
xml.writeAttribute( QLatin1String("property"), KUrl(cTerm.property().uri()).url() );
|
|
xml.writeAttribute( QLatin1String("comparator"), Nepomuk::Query::comparatorToString(cTerm.comparator()) );
|
|
if( !cTerm.variableName().isEmpty() )
|
|
xml.writeAttribute( QLatin1String("varname"), cTerm.variableName() );
|
|
if( cTerm.aggregateFunction() != ComparisonTerm::NoAggregateFunction )
|
|
xml.writeAttribute( QLatin1String("aggregate"), aggregateToString(cTerm.aggregateFunction()) );
|
|
if( cTerm.sortWeight() != 0 ) {
|
|
xml.writeAttribute( QLatin1String("sortWeight"), QString::number(cTerm.sortWeight()) );
|
|
xml.writeAttribute( QLatin1String("sortOrder"), cTerm.sortOrder() == Qt::AscendingOrder ? QLatin1String("asc") : QLatin1String("desc") );
|
|
}
|
|
xml.writeAttribute( QLatin1String("inverted"), cTerm.isInverted() ? QLatin1String("true") : QLatin1String("false") );
|
|
|
|
doSerializeTerm( xml, cTerm.subTerm() );
|
|
|
|
xml.writeEndElement();
|
|
break;
|
|
}
|
|
|
|
case Term::ResourceType:
|
|
xml.writeStartElement( QLatin1String("type") );
|
|
xml.writeAttribute( QLatin1String("uri"), KUrl( term.toResourceTypeTerm().type().uri() ).url() );
|
|
xml.writeEndElement();
|
|
break;
|
|
|
|
case Term::Negation:
|
|
xml.writeStartElement( QLatin1String("not") );
|
|
doSerializeTerm( xml, term.toNegationTerm().subTerm() );
|
|
xml.writeEndElement();
|
|
break;
|
|
|
|
case Term::Optional:
|
|
xml.writeStartElement( QLatin1String("optional") );
|
|
doSerializeTerm( xml, term.toOptionalTerm().subTerm() );
|
|
xml.writeEndElement();
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
Term doParseTerm( QXmlStreamReader& xml, bool* ok = 0 )
|
|
{
|
|
Q_ASSERT( xml.isStartElement() );
|
|
|
|
if( xml.name() == QLatin1String("literal") ) {
|
|
if( ok )
|
|
*ok = true;
|
|
QXmlStreamAttributes attr = xml.attributes();
|
|
LiteralTerm term;
|
|
if( attr.hasAttribute( QLatin1String("datatype") ) )
|
|
term = LiteralTerm( Soprano::LiteralValue::fromString( xml.readElementText(), KUrl(attr.value( QLatin1String("datatype") ).toString()) ) );
|
|
else
|
|
term = LiteralTerm( Soprano::LiteralValue::createPlainLiteral( xml.readElementText(), attr.value( QLatin1String("lang") ).toString()) );
|
|
// no need to skip to next element due to usage of readElementText?
|
|
return term;
|
|
}
|
|
|
|
else if( xml.name() == QLatin1String("resource") ) {
|
|
if( ok )
|
|
*ok = true;
|
|
ResourceTerm term( KUrl(xml.attributes().value( QLatin1String("uri") ).toString()) );
|
|
xml.readNextStartElement(); // skip to next element
|
|
return term;
|
|
}
|
|
|
|
else if( xml.name() == QLatin1String("and") ) {
|
|
AndTerm term;
|
|
while( xml.readNextStartElement() ) {
|
|
term.addSubTerm( doParseTerm( xml, ok ) );
|
|
if( ok && !*ok )
|
|
return Term();
|
|
}
|
|
return term;
|
|
}
|
|
|
|
else if( xml.name() == QLatin1String("or") ) {
|
|
OrTerm term;
|
|
while( xml.readNextStartElement() ) {
|
|
term.addSubTerm( doParseTerm( xml, ok ) );
|
|
if( ok && !*ok )
|
|
return Term();
|
|
}
|
|
return term;
|
|
}
|
|
|
|
else if( xml.name() == QLatin1String("not") ) {
|
|
if( xml.readNextStartElement() ) {
|
|
Term term = doParseTerm( xml, ok );
|
|
if( ok && !*ok )
|
|
return Term();
|
|
xml.readNextStartElement(); // skip to next element
|
|
return NegationTerm::negateTerm(term);
|
|
}
|
|
else {
|
|
if( ok )
|
|
*ok = false;
|
|
return Term();
|
|
}
|
|
}
|
|
|
|
else if( xml.name() == QLatin1String("optional") ) {
|
|
if( xml.readNextStartElement() ) {
|
|
Term term = doParseTerm( xml, ok );
|
|
if( ok && !*ok )
|
|
return Term();
|
|
xml.readNextStartElement(); // skip to next element
|
|
return OptionalTerm::optionalizeTerm(term);
|
|
}
|
|
else {
|
|
if( ok )
|
|
*ok = false;
|
|
return Term();
|
|
}
|
|
}
|
|
|
|
else if( xml.name() == QLatin1String("type") ) {
|
|
if( ok )
|
|
*ok = true;
|
|
ResourceTypeTerm term( KUrl(xml.attributes().value( QLatin1String("uri") ).toString()) );
|
|
xml.readNextStartElement(); // skip to next element
|
|
return term;
|
|
}
|
|
|
|
else if( xml.name() == QLatin1String("comparison") ) {
|
|
ComparisonTerm cTerm;
|
|
|
|
QXmlStreamAttributes attr = xml.attributes();
|
|
|
|
if( attr.hasAttribute( QLatin1String("property") ) )
|
|
cTerm.setProperty( KUrl(attr.value( QLatin1String("property")).toString()) );
|
|
|
|
if( attr.hasAttribute( QLatin1String("comparator") ) )
|
|
cTerm.setComparator( Nepomuk::Query::stringToComparator(attr.value( QLatin1String("comparator"))) );
|
|
|
|
if( attr.hasAttribute( QLatin1String("varname") ) )
|
|
cTerm.setVariableName( attr.value( QLatin1String("varname")).toString() );
|
|
|
|
if( attr.hasAttribute( QLatin1String("aggregate") ) )
|
|
cTerm.setAggregateFunction( stringToAggregate(attr.value( QLatin1String("aggregate"))) );
|
|
|
|
if( attr.hasAttribute( QLatin1String("sortWeight") ) )
|
|
cTerm.setSortWeight( attr.value( QLatin1String("sortWeight")).toString().toInt(),
|
|
attr.value( QLatin1String("sortOrder")) == QLatin1String("desc") ? Qt::DescendingOrder : Qt::AscendingOrder );
|
|
|
|
if( attr.hasAttribute( QLatin1String("inverted") ) )
|
|
cTerm.setInverted( attr.value( QLatin1String("inverted")) == QLatin1String("true") );
|
|
|
|
if( xml.readNextStartElement() ) {
|
|
Term term = doParseTerm( xml, ok );
|
|
if( ok && !*ok )
|
|
return Term();
|
|
cTerm.setSubTerm(term);
|
|
xml.readNextStartElement(); // skip to next element
|
|
}
|
|
|
|
if( ok )
|
|
*ok = true;
|
|
return cTerm;
|
|
}
|
|
|
|
else {
|
|
kDebug() << "Unknown term type" << xml.name();
|
|
if( ok )
|
|
*ok = false;
|
|
return Term();
|
|
}
|
|
}
|
|
|
|
|
|
Query::QueryFlags parseFlags( const QString& s )
|
|
{
|
|
Query::QueryFlags flags = Query::NoQueryFlags;
|
|
QStringList sl = s.split( QLatin1String("|"), QString::SkipEmptyParts );
|
|
Q_FOREACH( const QString& sf, sl ) {
|
|
if( sf == QLatin1String("NoResultRestrictions") )
|
|
flags |= Query::NoResultRestrictions;
|
|
else if( sf == QLatin1String("WithoutFullTextExcerpt") )
|
|
flags |= Query::WithoutFullTextExcerpt;
|
|
else
|
|
kError() << "Unknown query flag:" << sf;
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
QString serializeFlags( Query::QueryFlags flags ) {
|
|
QStringList sl;
|
|
if( flags&Query::NoResultRestrictions )
|
|
sl << QLatin1String("NoResultRestrictions");
|
|
if( flags&Query::WithoutFullTextExcerpt )
|
|
sl << QLatin1String("WithoutFullTextExcerpt");
|
|
return sl.join( QLatin1String("|") );
|
|
}
|
|
|
|
void readQueryAttributes( const QXmlStreamAttributes& attributes, Query& query )
|
|
{
|
|
if( attributes.hasAttribute( QLatin1String("limit") ) )
|
|
query.setLimit( attributes.value( QLatin1String("limit") ).toString().toInt() );
|
|
if( attributes.hasAttribute( QLatin1String("offset") ) )
|
|
query.setOffset( attributes.value( QLatin1String("offset") ).toString().toInt() );
|
|
if( attributes.hasAttribute( QLatin1String("fullTextScoring") ) )
|
|
query.setFullTextScoringEnabled( attributes.value( QLatin1String("fullTextScoring") ) == QLatin1String("true") );
|
|
if( attributes.hasAttribute( QLatin1String("fullTextScoringOrder") ) )
|
|
query.setFullTextScoringSortOrder( attributes.value( QLatin1String("fullTextScoringOrder") ) == QLatin1String("desc") ? Qt::DescendingOrder : Qt::AscendingOrder );
|
|
if( attributes.hasAttribute( QLatin1String("flags") ) )
|
|
query.setQueryFlags( parseFlags(attributes.value( QLatin1String("flags") ).toString() ) );
|
|
}
|
|
}
|
|
|
|
|
|
QString Nepomuk::Query::serializeQuery( const Query& query )
|
|
{
|
|
QString s;
|
|
QXmlStreamWriter xml( &s );
|
|
|
|
xml.writeStartDocument();
|
|
|
|
if( query.isFileQuery() ) {
|
|
FileQuery fq( query );
|
|
xml.writeStartElement( QLatin1String("filequery") );
|
|
xml.writeAttribute( QLatin1String("queryFiles"), fq.fileMode()&FileQuery::QueryFiles ? QLatin1String("true") : QLatin1String("false") );
|
|
xml.writeAttribute( QLatin1String("queryFolders"), fq.fileMode()&FileQuery::QueryFolders ? QLatin1String("true") : QLatin1String("false") );
|
|
}
|
|
else {
|
|
xml.writeStartElement( QLatin1String("query") );
|
|
}
|
|
|
|
xml.writeAttribute( QLatin1String("limit"), QString::number(query.limit()) );
|
|
xml.writeAttribute( QLatin1String("offset"), QString::number(query.offset()) );
|
|
xml.writeAttribute( QLatin1String("fullTextScoring"), query.fullTextScoringEnabled() ? QLatin1String("true") : QLatin1String("false") );
|
|
xml.writeAttribute( QLatin1String("fullTextScoringOrder"), query.fullTextScoringSortOrder() == Qt::AscendingOrder ? QLatin1String("asc") : QLatin1String("desc") );
|
|
xml.writeAttribute( QLatin1String("flags"), serializeFlags( query.queryFlags() ) );
|
|
|
|
Q_FOREACH( const Query::RequestProperty& rp, query.requestProperties() ) {
|
|
xml.writeStartElement( QLatin1String("requestProperty") );
|
|
xml.writeAttribute( QLatin1String("uri"), KUrl(rp.property().uri()).url() );
|
|
xml.writeAttribute( QLatin1String("optional"), rp.optional() ? QLatin1String("true") : QLatin1String("false") );
|
|
xml.writeEndElement();
|
|
}
|
|
|
|
if( query.isFileQuery() ) {
|
|
FileQuery fq( query );
|
|
const QHash<KUrl, bool> includeFolders = fq.allIncludeFolders();
|
|
for( QHash<KUrl, bool>::ConstIterator it = includeFolders.constBegin();
|
|
it != includeFolders.constEnd(); ++it ) {
|
|
xml.writeStartElement( QLatin1String("folder") );
|
|
xml.writeAttribute( QLatin1String("url"), it.key().url() );
|
|
xml.writeAttribute( QLatin1String("include"), QLatin1String("true") );
|
|
xml.writeAttribute( QLatin1String("recursive"), it.value() ? QLatin1String("true") : QLatin1String("false") );
|
|
xml.writeEndElement();
|
|
}
|
|
Q_FOREACH( const KUrl& url, fq.excludeFolders() ) {
|
|
xml.writeStartElement( QLatin1String("folder") );
|
|
xml.writeAttribute( QLatin1String("url"), url.url() );
|
|
xml.writeAttribute( QLatin1String("include"), QLatin1String("false") );
|
|
xml.writeEndElement();
|
|
}
|
|
}
|
|
|
|
doSerializeTerm( xml, query.term() );
|
|
|
|
// close query element
|
|
xml.writeEndElement();
|
|
xml.writeEndDocument();
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
Query Nepomuk::Query::parseQuery( const QString& s )
|
|
{
|
|
QXmlStreamReader xml( s );
|
|
|
|
Query query;
|
|
|
|
while( xml.readNextStartElement() ) {
|
|
if( xml.name() == QLatin1String("query") ) {
|
|
readQueryAttributes( xml.attributes(), query );
|
|
}
|
|
else if( xml.name() == QLatin1String("filequery") ) {
|
|
readQueryAttributes( xml.attributes(), query );
|
|
|
|
FileQuery fileQuery(query);
|
|
FileQuery::FileMode mode;
|
|
if( xml.attributes().value(QLatin1String("queryFiles") ) == QLatin1String("true") )
|
|
mode |= FileQuery::QueryFiles;
|
|
if( xml.attributes().value(QLatin1String("queryFolders") ) == QLatin1String("true") )
|
|
mode |= FileQuery::QueryFolders;
|
|
fileQuery.setFileMode(mode);
|
|
|
|
query = fileQuery;
|
|
}
|
|
else if( xml.name() == QLatin1String("requestProperty") ) {
|
|
query.addRequestProperty( Query::RequestProperty( KUrl(xml.attributes().value( QLatin1String("uri") ).toString()),
|
|
xml.attributes().value( QLatin1String("optional") ) == QLatin1String("true") ) );
|
|
xml.readNextStartElement(); // skip to next element
|
|
}
|
|
else if( xml.name() == QLatin1String("folder") ) {
|
|
if( !query.isFileQuery() ) {
|
|
kDebug() << "Folder tokens are only allowed in filequerys";
|
|
query = Query();
|
|
break;
|
|
}
|
|
|
|
FileQuery fileQuery(query);
|
|
if( xml.attributes().value( QLatin1String("include") ) == QLatin1String("true") ) {
|
|
// for the recursive flag we use double negation since the default is true (even if the flag is not specified)
|
|
fileQuery.addIncludeFolder( KUrl( xml.attributes().value( QLatin1String("url") ).toString() ),
|
|
xml.attributes().value( QLatin1String("recursive") ) != QLatin1String("false") );
|
|
}
|
|
else {
|
|
fileQuery.addExcludeFolder( KUrl( xml.attributes().value( QLatin1String("url") ).toString() ) );
|
|
}
|
|
query = fileQuery;
|
|
xml.readNextStartElement(); // skip to next element
|
|
}
|
|
else {
|
|
bool termOk = false;
|
|
query.setTerm( doParseTerm( xml, &termOk ) );
|
|
if( !termOk ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
|
|
QString Nepomuk::Query::serializeTerm( const Term& term )
|
|
{
|
|
QString s;
|
|
QXmlStreamWriter xml( &s );
|
|
|
|
// FIXME: should we really add the document details?
|
|
xml.writeStartDocument();
|
|
doSerializeTerm( xml, term );
|
|
xml.writeEndDocument();
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
Term Nepomuk::Query::parseTerm( const QString& s )
|
|
{
|
|
QXmlStreamReader xml( s );
|
|
if( xml.readNextStartElement() )
|
|
return doParseTerm( xml );
|
|
else
|
|
return Term();
|
|
|
|
}
|