60 changes: 52 additions & 8 deletions src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,6 @@ class CORE_EXPORT QgsExpression

//! Return the parsed expression as a string - useful for debugging
QString dump() const;
//! Creates ogc filter xml document. Supports minimum standard filter according to the OGC filter specs (=,!=,<,>,<=,>=,AND,OR,NOT)
//! @return true in case of success. False if string contains something that goes beyond the minimum standard filter
bool toOGCFilter( QDomDocument& doc ) const;

//! Return calculator used for distance and area calculations
//! (used by internal functions)
Expand Down Expand Up @@ -211,6 +208,8 @@ class CORE_EXPORT QgsExpression

//////

class Visitor; // visitor interface is defined below

class Node
{
public:
Expand All @@ -227,7 +226,9 @@ class CORE_EXPORT QgsExpression

virtual QStringList referencedColumns() const = 0;
virtual bool needsGeometry() const = 0;
virtual bool toOGCFilter( QDomDocument& doc, QDomElement& parent ) const { Q_UNUSED( doc ); Q_UNUSED( parent ); return false; }

// support for visitor pattern
virtual void accept( Visitor& v ) = 0;
};

class NodeList
Expand All @@ -250,12 +251,16 @@ class CORE_EXPORT QgsExpression
NodeUnaryOperator( UnaryOperator op, Node* operand ) : mOp( op ), mOperand( operand ) {}
~NodeUnaryOperator() { delete mOperand; }

UnaryOperator op() { return mOp; }
Node* operand() { return mOperand; }

virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;
virtual QStringList referencedColumns() const { return mOperand->referencedColumns(); }
virtual bool needsGeometry() const { return mOperand->needsGeometry(); }
virtual bool toOGCFilter( QDomDocument& doc, QDomElement& parent ) const;
virtual void accept( Visitor& v ) { v.visit( this ); }

protected:
UnaryOperator mOp;
Node* mOperand;
Expand All @@ -267,12 +272,16 @@ class CORE_EXPORT QgsExpression
NodeBinaryOperator( BinaryOperator op, Node* opLeft, Node* opRight ) : mOp( op ), mOpLeft( opLeft ), mOpRight( opRight ) {}
~NodeBinaryOperator() { delete mOpLeft; delete mOpRight; }

BinaryOperator op() { return mOp; }
Node* opLeft() { return mOpLeft; }
Node* opRight() { return mOpRight; }

virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;
virtual QStringList referencedColumns() const { return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); }
virtual bool needsGeometry() const { return mOpLeft->needsGeometry() || mOpRight->needsGeometry(); }
virtual bool toOGCFilter( QDomDocument& doc, QDomElement& parent ) const;
virtual void accept( Visitor& v ) { v.visit( this ); }

protected:
bool compare( double diff );
Expand All @@ -290,11 +299,17 @@ class CORE_EXPORT QgsExpression
NodeInOperator( Node* node, NodeList* list, bool notin = false ) : mNode( node ), mList( list ), mNotIn( notin ) {}
~NodeInOperator() { delete mNode; delete mList; }

Node* node() { return mNode; }
bool isNotIn() { return mNotIn; }
NodeList* list() { return mList; }

virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;
virtual QStringList referencedColumns() const { QStringList lst( mNode->referencedColumns() ); foreach( Node* n, mList->list() ) lst.append( n->referencedColumns() ); return lst; }
virtual bool needsGeometry() const { bool needs = false; foreach( Node* n, mList->list() ) needs |= n->needsGeometry(); return needs; }
virtual void accept( Visitor& v ) { v.visit( this ); }

protected:
Node* mNode;
NodeList* mList;
Expand All @@ -308,11 +323,16 @@ class CORE_EXPORT QgsExpression
//NodeFunction( QString name, NodeList* args ) : mName(name), mArgs(args) {}
~NodeFunction() { delete mArgs; }

int fnIndex() { return mFnIndex; }
NodeList* args() { return mArgs; }

virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;
virtual QStringList referencedColumns() const { QStringList lst; if ( !mArgs ) return lst; foreach( Node* n, mArgs->list() ) lst.append( n->referencedColumns() ); return lst; }
virtual bool needsGeometry() const { bool needs = BuiltinFunctions()[mFnIndex].mUsesGeometry; if ( mArgs ) { foreach( Node* n, mArgs->list() ) needs |= n->needsGeometry(); } return needs; }
virtual void accept( Visitor& v ) { v.visit( this ); }

protected:
//QString mName;
int mFnIndex;
Expand All @@ -324,12 +344,15 @@ class CORE_EXPORT QgsExpression
public:
NodeLiteral( QVariant value ) : mValue( value ) {}

QVariant value() { return mValue; }

virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;
virtual QStringList referencedColumns() const { return QStringList(); }
virtual bool needsGeometry() const { return false; }
virtual bool toOGCFilter( QDomDocument& doc, QDomElement& parent ) const;
virtual void accept( Visitor& v ) { v.visit( this ); }

protected:
QVariant mValue;
};
Expand All @@ -339,17 +362,38 @@ class CORE_EXPORT QgsExpression
public:
NodeColumnRef( QString name ) : mName( name ), mIndex( -1 ) {}

QString name() { return mName; }

virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields );
virtual QVariant eval( QgsExpression* parent, QgsFeature* f );
virtual QString dump() const;
virtual QStringList referencedColumns() const { return QStringList( mName ); }
virtual bool needsGeometry() const { return false; }
virtual bool toOGCFilter( QDomDocument& doc, QDomElement& parent ) const;
virtual void accept( Visitor& v ) { v.visit( this ); }

protected:
QString mName;
int mIndex;
};

//////

/** support for visitor pattern - algorithms dealing with the expressions
may be implemented without modifying the Node classes */
class Visitor
{
public:
virtual ~Visitor() {}
virtual void visit( NodeUnaryOperator* n ) = 0;
virtual void visit( NodeBinaryOperator* n ) = 0;
virtual void visit( NodeInOperator* n ) = 0;
virtual void visit( NodeFunction* n ) = 0;
virtual void visit( NodeLiteral* n ) = 0;
virtual void visit( NodeColumnRef* n ) = 0;
};

/** entry function for the visitor pattern */
void acceptVisitor( Visitor& v );

protected:

Expand Down
2 changes: 1 addition & 1 deletion src/gui/qgssearchquerybuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ void QgsSearchQueryBuilder::loadQuery()
QString newQueryText = query;

#if 0
// TODO: QgsExpression does not support overwriting of existing expressions
// TODO: implement with visitor pattern in QgsExpression

QStringList attributes = searchTree->referencedColumns();
QMap< QString, QString> attributesToReplace;
Expand Down
1 change: 1 addition & 0 deletions src/providers/wfs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ SET(WFS_SRCS
qgswfsdataitems.cpp
qgswfsdata.cpp
qgswfssourceselect.cpp
qgswfsutils.cpp
)

SET (WFS_MOC_HDRS
Expand Down
3 changes: 2 additions & 1 deletion src/providers/wfs/qgswfsconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "qgsexpression.h"
#include "qgslogger.h"
#include "qgsnetworkaccessmanager.h"
#include "qgswfsutils.h"
#include <QDomDocument>
#include <QNetworkRequest>
#include <QNetworkReply>
Expand Down Expand Up @@ -69,7 +70,7 @@ QString QgsWFSConnection::uriGetFeature( QString typeName, QString crsString, QS
{
//if not, if must be a QGIS expression
QgsExpression filterExpression( filter );
if ( !filterExpression.toOGCFilter( filterDoc ) )
if ( !QgsWFSUtils::expressionToOGCFilter( filterExpression, filterDoc ) )
{
//error
}
Expand Down
111 changes: 111 additions & 0 deletions src/providers/wfs/qgswfsutils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include "qgswfsutils.h"

#include "qgsexpression.h"


class QgsExpressionOGCVisitor : public QgsExpression::Visitor
{
public:
QgsExpressionOGCVisitor( QDomDocument& doc, QDomElement& parent )
: mDoc( doc ), mParent( parent ), mResult( false )
{}

bool result() { return mResult; }

void visit( QgsExpression::NodeUnaryOperator* n )
{
mResult = false;

if ( n->op() == QgsExpression::uoNot && n->operand() )
{
QDomElement notElemParent = mParent;
QDomElement notElem = mDoc.createElement( "Not" );

mParent = notElem;
n->operand()->accept( *this );
if ( !mResult )
return; // visit failed

mParent = notElemParent;
mParent.appendChild( notElem );
mResult = true;
}
}

void visit( QgsExpression::NodeBinaryOperator* n )
{
QString opName;
switch ( n->op() )
{
case QgsExpression::boEQ: opName = "PropertyIsEqualTo"; break;
case QgsExpression::boNE: opName = "PropertyIsNotEqualTo"; break;
case QgsExpression::boLE: opName = "PropertyIsLessThanOrEqualTo"; break;
case QgsExpression::boGE: opName = "PropertyIsLessThanOrEqualTo"; break;
case QgsExpression::boLT: opName = "PropertyIsLessThan"; break;
case QgsExpression::boGT: opName = "PropertyIsGreaterThan"; break;
case QgsExpression::boOr: opName = "Or"; break;
case QgsExpression::boAnd: opName = "And"; break;
default: break;
}

mResult = false;
if ( opName.isEmpty() || !n->opLeft() || !n->opRight() )
return; // unknown operation -> fail

QDomElement opElem = mDoc.createElement( opName );
QDomElement opElemParent = mParent;

mParent = opElem;
n->opLeft()->accept( *this );
if ( !mResult )
return; // visit failed

mParent = opElem;
n->opRight()->accept( *this );
if ( !mResult )
return; // visit failed

mParent = opElemParent;
mParent.appendChild( opElem );
mResult = true;
}

void visit( QgsExpression::NodeInOperator* ) { mResult = false; }
void visit( QgsExpression::NodeFunction* ) { mResult = false; }

void visit( QgsExpression::NodeLiteral* n )
{
QDomElement literalElem = mDoc.createElement( "Literal" );
QDomText literalText = mDoc.createTextNode( n->value().toString() );
literalElem.appendChild( literalText );
mParent.appendChild( literalElem );
mResult = true;
}

void visit( QgsExpression::NodeColumnRef* n )
{
QDomElement propertyElem = mDoc.createElement( "PropertyName" );
QDomText propertyText = mDoc.createTextNode( n->name() );
propertyElem.appendChild( propertyText );
mParent.appendChild( propertyElem );
mResult = true;
}

protected:
QDomDocument mDoc;
QDomElement mParent;
bool mResult;

};


bool QgsWFSUtils::expressionToOGCFilter( QgsExpression& exp, QDomDocument& doc )
{
doc.clear();
QDomElement filterElem = doc.createElement( "Filter" );
doc.appendChild( filterElem );

QgsExpressionOGCVisitor v( doc, filterElem );
exp.acceptVisitor( v );
return v.result();
}
16 changes: 16 additions & 0 deletions src/providers/wfs/qgswfsutils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef QGSWFSUTILS_H
#define QGSWFSUTILS_H

class QgsExpression;
class QDomDocument;

class QgsWFSUtils
{
public:

//! Creates ogc filter xml document. Supports minimum standard filter according to the OGC filter specs (=,!=,<,>,<=,>=,AND,OR,NOT)
//! @return true in case of success. False if string contains something that goes beyond the minimum standard filter
static bool expressionToOGCFilter( QgsExpression& exp, QDomDocument& doc );
};

#endif // QGSWFSUTILS_H