[API] removed QgsSearchString - QgsExpression should be used instead

This commit is contained in:
Martin Dobias 2012-07-15 11:14:53 +02:00
parent a0a0e04a39
commit e35b115881
12 changed files with 2 additions and 2193 deletions

View File

@ -73,8 +73,6 @@
%Include qgsrenderer.sip
%Include qgsrunprocess.sip
%Include qgsscalecalculator.sip
%Include qgssearchstring.sip
%Include qgssearchtreenode.sip
%Include qgssinglesymbolrenderer.sip
%Include qgssnapper.sip
%Include qgsspatialindex.sip

View File

@ -1,46 +0,0 @@
class QgsSearchString
{
%TypeHeaderCode
#include "qgssearchstring.h"
%End
public:
//! constructor
QgsSearchString();
//! construct and parse a string
//! @note added in v1.6
QgsSearchString( const QString & str );
//! copy constructor - makes also copy of search tree
QgsSearchString( const QgsSearchString& str );
//! destructor - deletes node tree
~QgsSearchString();
//! assignment operator takes care to copy search tree correctly
// unable to wrap QgsSearchString& operator=( const QgsSearchString& str );
/** sets search string and parses search tree
on success returns true and sets member variables to the new values */
bool setString( QString str );
/** copies tree and makes search string for it
on success returns true and sets member variables to the new values */
bool setTree( QgsSearchTreeNode* tree );
//! getter functions
QgsSearchTreeNode* tree();
QString string();
//! returns parser error message - valid only after unsuccessfull parsing
const QString& parserErrorMsg();
//! returns true if no string is set
bool isEmpty();
//! clear search string
void clear();
};

View File

@ -1,210 +0,0 @@
class QgsSearchTreeNode
{
%TypeHeaderCode
#include "qgssearchtreenode.h"
%End
public:
//! defines possible types of node
enum Type
{
tOperator = 1,
tNumber,
tColumnRef,
tString,
tNodeList,
};
//! possible operators
enum Operator
{
// binary
opAND = 1,
opOR,
opNOT,
// arithmetic
opPLUS,
opMINUS,
opMUL,
opMOD,
opDIV,
opPOW,
opSQRT,
opSIN,
opCOS,
opTAN,
opASIN,
opACOS,
opATAN,
opATAN2,
// conversion
opTOINT,
opTOREAL,
opTOSTRING,
// coordinates
opX,
opY,
opXAT,
opYAT,
// measuring
opLENGTH,
opAREA,
opPERIMETER,
// feature id
opID,
// comparison
opISNULL, // IS NULL
opISNOTNULL, // IS NOT NULL
opEQ, // =
opNE, // != resp. <>
opGT, // >
opLT, // <
opGE, // >=
opLE, // <=
opRegexp, // ~
opLike, // LIKE
opILike, // ILIKE
opIN, // IN
opNOTIN, // NOT IN
// string handling
opCONCAT,
opLOWER,
opUPPER,
opREPLACE,
opREGEXPREPLACE,
opSTRLEN,
opSUBSTR,
opROWNUM
};
//! constructors
QgsSearchTreeNode( double number );
QgsSearchTreeNode( Operator o, QgsSearchTreeNode* left, QgsSearchTreeNode* right );
QgsSearchTreeNode( QString text, bool isColumnRef );
//! copy contructor - copies whole tree!
QgsSearchTreeNode( const QgsSearchTreeNode& node );
//! destructor - deletes children nodes (if any)
~QgsSearchTreeNode();
//! returns type of current node
Type type() const;
//! node value getters
Operator op() const;
double number() const;
QString columnRef() const;
QString string() const;
//! node value setters (type is set also)
void setOp( Operator o );
void setNumber( double number );
void setColumnRef( const QString& str );
void setString( const QString& str );
//! children
QgsSearchTreeNode* Left();
QgsSearchTreeNode* Right();
void setLeft( QgsSearchTreeNode* left );
void setRight( QgsSearchTreeNode* right );
//! returns search string that should be equal to original parsed string
QString makeSearchString();
//! checks whether the node tree is valid against supplied attributes
//! @note attributes and optional geom parameter replace with feature in 1.6
bool checkAgainst( const QMap<int,QgsField>& fields, QgsFeature &f );
//! @deprecated
bool checkAgainst( const QMap<int,QgsField>& fields, const QMap<int, QVariant>& attributes, QgsGeometry* geom = 0 ) /Deprecated/;
//! checks if there were errors during evaluation
bool hasError();
//! returns error message
const QString& errorMsg();
//! wrapper around valueAgainst()
//! @note added in 1.4
//! @note attribute/geom replaced by feature in 1.6
bool getValue( QgsSearchTreeValue& value /Out/, QgsSearchTreeNode* node,
const QMap<int,QgsField>& fields, QgsFeature &f );
//! @deprecated
bool getValue( QgsSearchTreeValue& value /Out/, QgsSearchTreeNode* node,
const QMap<int,QgsField>& fields, const QMap<int,QVariant>& attributes, QgsGeometry* geom = 0 ) /Deprecated/;
//! return a list of referenced columns in the tree
//! @note added in 1.5
QStringList referencedColumns();
//! return a list of all attribute nodes
//! @note added in 1.5
QList<QgsSearchTreeNode*> columnRefNodes();
//! check whether there are any operators that need geometry (for area, length)
//! @note added in 1.5
bool needsGeometry();
//! return quoted column reference (in double quotes)
//! @note added in 1.5
static QString quotedColumnRef( QString name );
//! Set current row number within this tree.
//! This value is stored only in the nodes being $rownum operator - in mNumber
//! @note added in 1.6
void setCurrentRowNumber( int rownum );
protected:
//! returns scalar value of node
//! @note attribute/geom replaced by feature in 1.6
QgsSearchTreeValue valueAgainst( const QMap<int,QgsField>& fields, QgsFeature &f );
//! @deprecated
QgsSearchTreeValue valueAgainst( const QMap<int,QgsField>& fields, const QMap<int,QVariant>& attributes, QgsGeometry* geom = 0 ) /Deprecated/;
//! strips mText when node is of string type
void stripText();
};
class QgsSearchTreeValue
{
%TypeHeaderCode
#include "qgssearchtreenode.h"
%End
public:
enum Type
{
valError,
valString,
valNumber
};
QgsSearchTreeValue();
QgsSearchTreeValue( QString string );
QgsSearchTreeValue( double number );
QgsSearchTreeValue( int error, QString errorMsg );
static QgsSearchTreeValue compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2,
Qt::CaseSensitivity = Qt::CaseSensitive );
bool isNumeric();
bool isError();
QString& string();
double number();
};

View File

@ -98,8 +98,6 @@ SET(QGIS_CORE_SRCS
qgsrectangle.cpp
qgsrunprocess.cpp
qgsscalecalculator.cpp
qgssearchstring.cpp
qgssearchtreenode.cpp
qgssnapper.cpp
qgscoordinatereferencesystem.cpp
qgstolerance.cpp
@ -244,9 +242,9 @@ IF (WITH_INTERNAL_SPATIALITE)
INCLUDE_DIRECTORIES(BEFORE spatialite/headers/spatialite)
ENDIF (WITH_INTERNAL_SPATIALITE)
ADD_FLEX_FILES(QGIS_CORE_SRCS qgssearchstringlexer.ll qgsexpressionlexer.ll)
ADD_FLEX_FILES(QGIS_CORE_SRCS qgsexpressionlexer.ll)
ADD_BISON_FILES(QGIS_CORE_SRCS qgssearchstringparser.yy qgsexpressionparser.yy)
ADD_BISON_FILES(QGIS_CORE_SRCS qgsexpressionparser.yy)
SET(QGIS_CORE_MOC_HDRS
qgsapplication.h
@ -362,8 +360,6 @@ SET(QGIS_CORE_HDRS
qgsrendercontext.h
qgsrunprocess.h
qgsscalecalculator.h
qgssearchstring.h
qgssearchtreenode.h
qgssnapper.h
qgscoordinatereferencesystem.h
qgsvectordataprovider.h

View File

@ -1,116 +0,0 @@
/***************************************************************************
qgssearchstring.cpp
interface for parsing and evaluation of search strings
--------------------
begin : 2005-07-26
copyright : (C) 2005 by Martin Dobias
email : won.der at centrum.sk
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgssearchstring.h"
#include "qgssearchtreenode.h"
//! global function from parser.y that interfaces parser
extern QgsSearchTreeNode* parseSearchString( const QString& str, QString& parserErrorMsg );
QgsSearchString::QgsSearchString()
{
mTree = NULL;
}
QgsSearchString::QgsSearchString( const QString & str )
{
mTree = NULL;
setString( str );
}
QgsSearchString::QgsSearchString( const QgsSearchString& str )
{
if ( str.mTree )
mTree = new QgsSearchTreeNode( *str.mTree );
else
mTree = NULL;
mString = str.mString;
}
QgsSearchString& QgsSearchString::operator=( const QgsSearchString & str )
{
clear();
if ( str.mTree )
mTree = new QgsSearchTreeNode( *str.mTree );
else
mTree = NULL;
mString = str.mString;
return *this;
}
QgsSearchString::~QgsSearchString()
{
delete mTree; // deletes complete tree
}
bool QgsSearchString::setString( QString str )
{
mParserErrorMsg.clear();
// empty string
if ( str.isEmpty() )
{
clear();
return true;
}
// calls external C function that does all parsing
QgsSearchTreeNode* tree = parseSearchString( str, mParserErrorMsg );
if ( tree )
{
delete mTree;
mTree = tree;
mString = str;
return true;
}
return false;
}
bool QgsSearchString::setTree( QgsSearchTreeNode* tree )
{
if ( tree == NULL )
{
clear();
}
else
{
delete mTree;
mTree = new QgsSearchTreeNode( *tree );
mString = mTree->makeSearchString();
}
return true;
}
bool QgsSearchString::isEmpty()
{
return ( mTree == NULL );
}
void QgsSearchString::clear()
{
delete mTree;
mTree = NULL;
mString.clear();
}

View File

@ -1,83 +0,0 @@
/***************************************************************************
qgssearchstring.h
interface for parsing and evaluation of search strings
--------------------
begin : 2005-07-26
copyright : (C) 2005 by Martin Dobias
email : won.der at centrum.sk
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSEARCHSTRING_H
#define QGSSEARCHSTRING_H
#include <QString>
class QgsSearchTreeNode;
/** \ingroup core
* A class to represent a search string.
* - interface for 'search string' parser
* - when a string is set, it parses it and creates parsed tree of it
* - owns node tree and coresponding search string
* - keeps string and node tree always in sync
*
*/
class CORE_EXPORT QgsSearchString
{
public:
//! construct an empty string
QgsSearchString();
//! construct and parse a string
//! @note added in v1.6
QgsSearchString( const QString & str );
//! copy constructor - makes also copy of search tree
QgsSearchString( const QgsSearchString& str );
//! destructor - deletes node tree
~QgsSearchString();
//! assignment operator takes care to copy search tree correctly
QgsSearchString& operator=( const QgsSearchString& str );
/** sets search string and parses search tree
on success returns true and sets member variables to the new values */
bool setString( QString str );
/* copies tree and makes search string for it
on success returns true and sets member variables to the new values */
bool setTree( QgsSearchTreeNode* tree );
//! getter functions
QgsSearchTreeNode* tree() { return mTree; }
QString string() { return mString; }
//! returns parser error message - valid only after unsuccessfull parsing
const QString& parserErrorMsg() { return mParserErrorMsg; }
//! returns true if no string is set
bool isEmpty();
//! clear search string
void clear();
private:
//! search string and coresponding tree
QgsSearchTreeNode* mTree;
QString mString;
//! error message from parser
QString mParserErrorMsg;
};
#endif

View File

@ -1,144 +0,0 @@
/***************************************************************************
qgssearchstringparser.ll
Rules for lexical analysis of search strings done by Flex
--------------------
begin : 2005-07-26
copyright : (C) 2005 by Martin Dobias
email : won.der at centrum.sk
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
%option noyywrap
%option case-insensitive
%option never-interactive
%option nounput
// ensure that lexer will be 8-bit (and not just 7-bit)
%option 8bit
%{
#include <stdlib.h> // atof()
#include "qgssearchtreenode.h"
#include "qgssearchstringparser.hpp"
// if not defined, searches for isatty()
// which doesn't in MSVC compiler
#define YY_NEVER_INTERACTIVE 1
#ifndef YY_NO_UNPUT
#define YY_NO_UNPUT // unused
#endif
#ifdef _MSC_VER
#define YY_NO_UNISTD_H
#endif
%}
white [ \t\r\n]+
non_ascii [\x80-\xFF]
col_first [A-Za-z_]|{non_ascii}
col_next [A-Za-z0-9_]|{non_ascii}
column_ref {col_first}{col_next}*
col_str_char "\"\""|[^\"]
column_ref_quoted "\""{col_str_char}*"\""
dig [0-9]
num1 {dig}+\.?([eE][-+]?{dig}+)?
num2 {dig}*\.{dig}+([eE][-+]?{dig}+)?
number {num1}|{num2}
str_char ('')|(\\.)|[^'\\]
string "'"{str_char}*"'"
%%
"NOT" { return NOT; }
"AND" { return AND; }
"OR" { return OR; }
"NULL" { return NULLVALUE; }
"IS" { return IS; }
"IN" { return IN; }
"=" { yylval.op = QgsSearchTreeNode::opEQ; return COMPARISON; }
"!=" { yylval.op = QgsSearchTreeNode::opNE; return COMPARISON; }
"<=" { yylval.op = QgsSearchTreeNode::opLE; return COMPARISON; }
">=" { yylval.op = QgsSearchTreeNode::opGE; return COMPARISON; }
"<>" { yylval.op = QgsSearchTreeNode::opNE; return COMPARISON; }
"<" { yylval.op = QgsSearchTreeNode::opLT; return COMPARISON; }
">" { yylval.op = QgsSearchTreeNode::opGT; return COMPARISON; }
"~" { yylval.op = QgsSearchTreeNode::opRegexp; return COMPARISON; }
"LIKE" { yylval.op = QgsSearchTreeNode::opLike; return COMPARISON; }
"ILIKE" { yylval.op = QgsSearchTreeNode::opILike; return COMPARISON; }
"sqrt" { yylval.op = QgsSearchTreeNode::opSQRT; return FUNCTION1;}
"sin" { yylval.op = QgsSearchTreeNode::opSIN; return FUNCTION1;}
"cos" { yylval.op = QgsSearchTreeNode::opCOS; return FUNCTION1;}
"tan" { yylval.op = QgsSearchTreeNode::opTAN; return FUNCTION1;}
"asin" { yylval.op = QgsSearchTreeNode::opASIN; return FUNCTION1;}
"acos" { yylval.op = QgsSearchTreeNode::opACOS; return FUNCTION1;}
"atan" { yylval.op = QgsSearchTreeNode::opATAN; return FUNCTION1;}
"to int" { yylval.op = QgsSearchTreeNode::opTOINT; return FUNCTION1;}
"to real" { yylval.op = QgsSearchTreeNode::opTOREAL; return FUNCTION1;}
"to string" { yylval.op = QgsSearchTreeNode::opTOSTRING; return FUNCTION1;}
"lower" { yylval.op = QgsSearchTreeNode::opLOWER; return FUNCTION1;}
"upper" { yylval.op = QgsSearchTreeNode::opUPPER; return FUNCTION1;}
"length" { yylval.op = QgsSearchTreeNode::opSTRLEN; return FUNCTION1;}
"xat" { yylval.op = QgsSearchTreeNode::opXAT; return FUNCTION1;}
"yat" { yylval.op = QgsSearchTreeNode::opYAT; return FUNCTION1;}
"atan2" { yylval.op = QgsSearchTreeNode::opATAN2; return FUNCTION2;}
"replace" { yylval.op = QgsSearchTreeNode::opREPLACE; return FUNCTION3;}
"regexp_replace" { yylval.op = QgsSearchTreeNode::opREGEXPREPLACE; return FUNCTION3;}
"substr" { yylval.op = QgsSearchTreeNode::opSUBSTR; return FUNCTION3;}
"||" { return CONCAT; }
[+-/*^%] { return yytext[0]; }
[()] { return yytext[0]; }
{number} { yylval.number = atof(yytext); return NUMBER; }
{string} { return STRING; }
"$rownum" { return ROWNUM; }
"$area" { return AREA; }
"$length" { return LENGTH; }
"$perimeter" { return PERIMETER; }
"$x" { return X; }
"$y" { return Y; }
"$id" { return ID; }
{column_ref} { return COLUMN_REF; }
{column_ref_quoted} { return COLUMN_REF; }
{white} /* skip blanks and tabs */
. { return Unknown_CHARACTER; }
%%
void set_input_buffer(const char* buffer)
{
yy_scan_string(buffer);
}

View File

@ -1,266 +0,0 @@
/***************************************************************************
qgssearchstringparser.yy
Grammar rules for search string used by Bison
--------------------
begin : 2005-07-26
copyright : (C) 2005 by Martin Dobias
email : won.der at centrum.sk
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
%{
#include <qglobal.h>
#include <QList>
#include <cstdlib>
#include "qgssearchtreenode.h"
#ifdef _MSC_VER
# pragma warning( disable: 4065 ) // switch statement contains 'default' but no 'case' labels
# pragma warning( disable: 4702 ) // unreachable code
#endif
// don't redeclare malloc/free
#define YYINCLUDED_STDLIB_H 1
/** returns parsed tree, otherwise returns NULL and sets parserErrorMsg
(interface function to be called from QgsSearchString)
*/
QgsSearchTreeNode* parseSearchString(const QString& str, QString& parserErrorMsg);
//! from lex.yy.c
extern int yylex();
extern char* yytext;
extern void set_input_buffer(const char* buffer);
//! varible where the parser error will be stored
QString gParserErrorMsg;
//! sets gParserErrorMsg
void yyerror(const char* msg);
//! temporary list for nodes without parent (if parsing fails these nodes are removed)
QList<QgsSearchTreeNode*> gTmpNodes;
void joinTmpNodes(QgsSearchTreeNode* parent, QgsSearchTreeNode* left, QgsSearchTreeNode* right);
void addToTmpNodes(QgsSearchTreeNode* node);
// we want verbose error messages
#define YYERROR_VERBOSE 1
%}
%union { QgsSearchTreeNode* node; double number; QgsSearchTreeNode::Operator op;}
%start root
/* http://docs.openlinksw.com/virtuoso/GRAMMAR.html */
%token <number> NUMBER
%token <op> COMPARISON
%token <op> FUNCTION1
%token <op> FUNCTION2
%token <op> FUNCTION3
%token CONCAT
%token IS
%token IN
%token ROWNUM
%token AREA
%token PERIMETER
%token LENGTH
%token X
%token Y
%token ID
%token NULLVALUE
%token STRING
%token COLUMN_REF
%token Unknown_CHARACTER
%token NOT
%token AND
%token OR
%type <node> root
%type <node> search_cond
%type <node> predicate
%type <node> comp_predicate
%type <node> scalar_exp
%type <node> scalar_exp_list
// debugging
//%error-verbose
// operator precedence
// all operators have left associativity, i.e. 1+2+3 translates to (1+2)+3
// the order of operators here determines their precedence
%left OR
%left AND
%left NOT
%left COMPARISON
%left CONCAT
%left IN
%left '+' '-'
%left '*' '/' '%'
%left '^'
%left UMINUS // fictitious symbol (for unary minus)
%%
root: search_cond { /*gParserRootNode = $1;*/ }
| scalar_exp {}
;
search_cond:
search_cond OR search_cond { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opOR, $1, $3); joinTmpNodes($$,$1,$3); }
| search_cond AND search_cond { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opAND, $1, $3); joinTmpNodes($$,$1,$3); }
| NOT search_cond { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opNOT, $2, 0); joinTmpNodes($$,$2, 0); }
| '(' search_cond ')' { $$ = $2; }
| predicate
;
// more predicates to come
predicate:
comp_predicate
| scalar_exp IN '(' scalar_exp_list ')' { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opIN, $1, $4); joinTmpNodes($$,$1,$4); }
| scalar_exp NOT IN '(' scalar_exp_list ')' { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opNOTIN, $1, $5); joinTmpNodes($$,$1,$5); }
;
comp_predicate:
scalar_exp IS NULLVALUE { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opISNULL, $1, 0); joinTmpNodes($$,$1,0); }
| scalar_exp IS NOT NULLVALUE { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opISNOTNULL, $1, 0); joinTmpNodes($$,$1,0); }
| scalar_exp COMPARISON scalar_exp { $$ = new QgsSearchTreeNode($2, $1, $3); joinTmpNodes($$,$1,$3); }
;
scalar_exp_list:
scalar_exp_list ',' scalar_exp { $$ = $1; $1->append($3); joinTmpNodes($1,$1,$3); }
| scalar_exp
{
$$ = new QgsSearchTreeNode( QgsSearchTreeNode::tNodeList );
$$->append($1);
joinTmpNodes($$,$1,0);
}
;
scalar_exp:
FUNCTION1 '(' scalar_exp ')' { $$ = new QgsSearchTreeNode($1, $3, 0); joinTmpNodes($$, $3, 0); }
| FUNCTION2 '(' scalar_exp ',' scalar_exp ')' { $$ = new QgsSearchTreeNode($1, $3, $5); joinTmpNodes($$, $3, $5); }
| FUNCTION3 '(' scalar_exp ',' scalar_exp ',' scalar_exp ')'
{
QgsSearchTreeNode *args = new QgsSearchTreeNode( QgsSearchTreeNode::tNodeList );
args->append($3);
args->append($5);
args->append($7);
$$ = new QgsSearchTreeNode($1, args, 0);
joinTmpNodes($$, $3, $5);
joinTmpNodes($$, $$, $7);
}
| scalar_exp '^' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPOW, $1, $3); joinTmpNodes($$,$1,$3); }
| scalar_exp '*' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opMUL, $1, $3); joinTmpNodes($$,$1,$3); }
| scalar_exp '%' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opMOD, $1, $3); joinTmpNodes($$,$1,$3); }
| scalar_exp '/' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opDIV, $1, $3); joinTmpNodes($$,$1,$3); }
| scalar_exp '+' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPLUS, $1, $3); joinTmpNodes($$,$1,$3); }
| scalar_exp '-' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opMINUS,$1, $3); joinTmpNodes($$,$1,$3); }
| '(' scalar_exp ')' { $$ = $2; }
| '+' scalar_exp %prec UMINUS { $$ = $2; }
| '-' scalar_exp %prec UMINUS
{
if ( $2->type() == QgsSearchTreeNode::tNumber )
{
$$ = $2;
$$->setNumber(- $$->number());
}
else
{
QgsSearchTreeNode *null = new QgsSearchTreeNode( 0.0 );
$$ = new QgsSearchTreeNode( QgsSearchTreeNode::opMINUS, null, $2);
joinTmpNodes($$, $2, 0);
}
}
| scalar_exp CONCAT scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opCONCAT, $1, $3); joinTmpNodes($$, $1, $3); }
| ROWNUM { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opROWNUM, 0, 0); addToTmpNodes($$); }
| AREA { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opAREA, 0, 0); addToTmpNodes($$); }
| PERIMETER { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPERIMETER, 0, 0); addToTmpNodes($$); }
| LENGTH { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opLENGTH, 0, 0); addToTmpNodes($$); }
| X { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opX, 0, 0); addToTmpNodes($$); }
| Y { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opY, 0, 0); addToTmpNodes($$); }
| ID { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opID, 0, 0); addToTmpNodes($$); }
| NUMBER { $$ = new QgsSearchTreeNode($1); addToTmpNodes($$); }
| STRING { $$ = new QgsSearchTreeNode(QString::fromUtf8(yytext), 0); addToTmpNodes($$); }
| NULLVALUE { $$ = new QgsSearchTreeNode(QString::null, 0); addToTmpNodes($$); }
| COLUMN_REF { $$ = new QgsSearchTreeNode(QString::fromUtf8(yytext), 1); addToTmpNodes($$); }
;
%%
void addToTmpNodes(QgsSearchTreeNode* node)
{
gTmpNodes.append(node);
}
void joinTmpNodes(QgsSearchTreeNode* parent, QgsSearchTreeNode* left, QgsSearchTreeNode* right)
{
bool res;
Q_UNUSED(res);
if (left)
{
res = gTmpNodes.removeAll(left);
Q_ASSERT(res);
}
if (right)
{
res = gTmpNodes.removeAll(right);
Q_ASSERT(res);
}
gTmpNodes.append(parent);
}
// returns parsed tree, otherwise returns NULL and sets parserErrorMsg
QgsSearchTreeNode* parseSearchString(const QString& str, QString& parserErrorMsg)
{
// list should be empty when starting
Q_ASSERT(gTmpNodes.count() == 0);
set_input_buffer(str.toUtf8().constData());
int res = yyparse();
// list should be empty when parsing was OK
if (res == 0) // success?
{
Q_ASSERT(gTmpNodes.count() == 1);
return gTmpNodes.takeFirst();
}
else // error?
{
parserErrorMsg = gParserErrorMsg;
// remove nodes without parents - to prevent memory leaks
while (gTmpNodes.size() > 0)
delete gTmpNodes.takeFirst();
return NULL;
}
}
void yyerror(const char* msg)
{
gParserErrorMsg = msg;
}

View File

@ -1,953 +0,0 @@
/***************************************************************************
qgssearchtreenode.cpp
Implementation for evaluating parsed tree
--------------------
begin : 2005-07-26
copyright : (C) 2005 by Martin Dobias
email : won.der at centrum.sk
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgslogger.h"
#include "qgsdistancearea.h"
#include "qgsfield.h"
#include "qgsgeometry.h"
#include "qgssearchtreenode.h"
#include <QRegExp>
#include <QObject>
#include <QSet>
#include <QSettings>
#include <cmath>
#define EVAL_STR(x) (x.length() ? x : "(empty)")
QgsSearchTreeNode::QgsSearchTreeNode( QgsSearchTreeNode::Type t )
{
Q_ASSERT( t == tNodeList );
mType = t;
mLeft = NULL;
mRight = NULL;
init();
}
QgsSearchTreeNode::QgsSearchTreeNode( double number )
{
mType = tNumber;
mNumber = number;
mLeft = NULL;
mRight = NULL;
init();
}
QgsSearchTreeNode::QgsSearchTreeNode( Operator op,
QgsSearchTreeNode* left,
QgsSearchTreeNode* right )
{
mType = tOperator;
mOp = op;
mLeft = left;
mRight = right;
init();
}
QgsSearchTreeNode::QgsSearchTreeNode( QString text, bool isColumnRef )
{
mLeft = NULL;
mRight = NULL;
if ( isColumnRef )
{
mType = tColumnRef;
mText = text;
if ( text.at( 0 ) == '\"' )
{
// column reference is quoted
stripColRef();
}
}
else
{
mType = tString;
mText = text;
stripText();
}
init();
}
QgsSearchTreeNode::QgsSearchTreeNode( const QgsSearchTreeNode& node )
{
mType = node.mType;
mOp = node.mOp;
mNumber = node.mNumber;
mText = node.mText;
// recursively copy children
if ( node.mLeft )
mLeft = new QgsSearchTreeNode( *node.mLeft );
else
mLeft = NULL;
if ( node.mRight )
mRight = new QgsSearchTreeNode( *node.mRight );
else
mRight = NULL;
foreach( QgsSearchTreeNode * lnode, node.mNodeList )
{
mNodeList.append( new QgsSearchTreeNode( *lnode ) );
}
init();
}
QgsSearchTreeNode::~QgsSearchTreeNode()
{
// delete children
if ( mLeft )
delete mLeft;
if ( mRight )
delete mRight;
while ( !mNodeList.isEmpty() )
delete mNodeList.takeFirst();
delete mCalc;
}
void QgsSearchTreeNode::init()
{
mCalc = NULL;
if ( mType == tOperator && ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER ) )
{
//initialize QgsDistanceArea
mCalc = new QgsDistanceArea;
mCalc->setProjectionsEnabled( false );
QSettings settings;
QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString();
mCalc->setEllipsoid( ellipsoid );
}
else if ( mType == tOperator && mOp == opROWNUM )
{
// initialize row number to a sane value
mNumber = 0;
}
}
void QgsSearchTreeNode::stripText()
{
// strip single quotes on start,end
mText = mText.mid( 1, mText.length() - 2 );
// make single "single quotes" from double "single quotes"
mText.replace( QRegExp( "''" ), "'" );
// strip \n \' etc.
int index = 0;
while (( index = mText.indexOf( '\\', index ) ) != -1 )
{
mText.remove( index, 1 ); // delete backslash
QChar chr;
switch ( mText[index].toLatin1() ) // evaluate backslashed character
{
case 'n': chr = '\n'; break;
case 't': chr = '\t'; break;
case '\\': chr = '\\'; break;
case '\'': chr = '\''; break;
default: chr = '?'; break;
}
mText[index++] = chr; // set new character and push index +1
}
}
void QgsSearchTreeNode::stripColRef()
{
// strip double quotes on start,end
mText = mText.mid( 1, mText.length() - 2 );
// make single "double quotes" from double "double quotes"
mText.replace( QRegExp( "\"\"" ), "\"" );
}
QString QgsSearchTreeNode::quotedColumnRef( QString name )
{
return QString( "\"%1\"" ).arg( name.replace( "\"", "\"\"" ) );
}
QString QgsSearchTreeNode::makeSearchString()
{
QString str;
if ( mType == tOperator )
{
if ( mOp == opSQRT || mOp == opSIN || mOp == opCOS || mOp == opTAN ||
mOp == opASIN || mOp == opACOS || mOp == opATAN ||
mOp == opTOINT || mOp == opTOREAL || mOp == opTOSTRING ||
mOp == opLOWER || mOp == opUPPER || mOp == opSTRLEN ||
mOp == opATAN2 || mOp == opREPLACE || mOp == opREGEXPREPLACE ||
mOp == opSUBSTR || mOp == opXAT || mOp == opYAT )
{
// functions
switch ( mOp )
{
case opSQRT: str += "sqrt"; break;
case opSIN: str += "sin"; break;
case opCOS: str += "cos"; break;
case opTAN: str += "tan"; break;
case opASIN: str += "asin"; break;
case opACOS: str += "acos"; break;
case opATAN: str += "atan"; break;
case opTOINT: str += "to int"; break;
case opTOREAL: str += "to real"; break;
case opTOSTRING: str += "to string"; break;
case opLOWER: str += "lower"; break;
case opUPPER: str += "upper"; break;
case opATAN2: str += "atan2"; break;
case opSTRLEN: str += "length"; break;
case opREPLACE: str += "replace"; break;
case opREGEXPREPLACE: str += "regexp_replace"; break;
case opSUBSTR: str += "substr"; break;
case opXAT: str += "xat"; break;
case opYAT: str += "yat"; break;
default: str += "?";
}
// currently all functions take one parameter
str += QString( "(%1)" ).arg( mLeft->makeSearchString() );
}
else if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opROWNUM || mOp == opID || mOp == opX || mOp == opY )
{
// special nullary opeators
switch ( mOp )
{
case opLENGTH: str += "$length"; break;
case opAREA: str += "$area"; break;
case opPERIMETER: str += "$perimeter"; break;
case opROWNUM: str += "$rownum"; break;
case opX: str += "$x"; break;
case opY: str += "$y"; break;
case opID: str += "$id"; break;
default: str += "?";
}
}
else if ( mOp == opNOT )
{
// unary NOT operator
str += "(NOT " + mLeft->makeSearchString() + ")";
}
else
{
// the rest of operator using infix notation
str += "(";
if ( mLeft )
{
str += mLeft->makeSearchString();
}
switch ( mOp )
{
case opAND: str += " AND "; break;
case opOR: str += " OR "; break;
case opPLUS: str += "+"; break;
case opMINUS: str += "-"; break;
case opMUL: str += "*"; break;
case opMOD: str += "%"; break;
case opDIV: str += "/"; break;
case opPOW: str += "^"; break;
case opEQ: str += " = "; break;
case opNE: str += " != "; break;
case opGT: str += " > "; break;
case opLT: str += " < "; break;
case opGE: str += " >= "; break;
case opLE: str += " <= "; break;
case opISNULL: str += " IS NULL"; break;
case opISNOTNULL: str += " IS NOT NULL"; break;
case opRegexp: str += " ~ "; break;
case opLike: str += " LIKE "; break;
case opILike: str += " ILIKE "; break;
case opIN: str += " IN "; break;
case opNOTIN: str += " NOT IN "; break;
case opCONCAT: str += " || "; break;
default: str += " ? ";
}
if ( mRight )
{
str += mRight->makeSearchString();
}
str += ")";
}
}
else if ( mType == tNumber )
{
str += QString::number( mNumber );
}
else if ( mType == tString || mType == tColumnRef )
{
str += mText;
}
else if ( mType == tNodeList )
{
QStringList items;
foreach( QgsSearchTreeNode * node, mNodeList )
{
items << node->makeSearchString();
}
str += "(" + items.join( "," ) + ")";
}
else // unknown type
{
str += "unknown_node_type:";
str += QString::number( mType );
}
return str;
}
QStringList QgsSearchTreeNode::referencedColumns()
{
QList<QgsSearchTreeNode*> columnNodeList = columnRefNodes();
QSet<QString> columnStringSet;
QList<QgsSearchTreeNode*>::const_iterator nodeIt = columnNodeList.constBegin();
for ( ; nodeIt != columnNodeList.constEnd(); ++nodeIt )
{
columnStringSet.insert(( *nodeIt )->columnRef() );
}
return columnStringSet.toList();
}
QList<QgsSearchTreeNode*> QgsSearchTreeNode::columnRefNodes()
{
QList<QgsSearchTreeNode*> nodeList;
if ( mType == tOperator )
{
if ( mLeft )
{
nodeList += mLeft->columnRefNodes();
}
if ( mRight )
{
nodeList += mRight->columnRefNodes();
}
}
else if ( mType == tColumnRef )
{
nodeList.push_back( this );
}
return nodeList;
}
bool QgsSearchTreeNode::needsGeometry()
{
if ( mType == tOperator )
{
if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY || mOp == opXAT || mOp == opYAT )
return true;
if ( mLeft && mLeft->needsGeometry() )
return true;
if ( mRight && mRight->needsGeometry() )
return true;
return false;
}
else
{
return false;
}
}
bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, const QgsAttributeMap &attributes, QgsGeometry* geom )
{
QgsFeature f;
f.setAttributeMap( attributes );
if ( geom )
f.setGeometry( *geom );
return checkAgainst( fields, f );
}
bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, QgsFeature &f )
{
QgsDebugMsgLevel( "checkAgainst: " + makeSearchString(), 2 );
mError = "";
// this error should be caught when checking syntax, but for sure...
if ( mType != tOperator )
{
mError = QObject::tr( "Expected operator, got scalar value!" );
return false;
}
QgsSearchTreeValue value1, value2;
switch ( mOp )
{
case opNOT:
return !mLeft->checkAgainst( fields, f );
case opAND:
if ( !mLeft->checkAgainst( fields, f ) )
return false;
return mRight->checkAgainst( fields, f );
case opOR:
if ( mLeft->checkAgainst( fields, f ) )
return true;
return mRight->checkAgainst( fields, f );
case opISNULL:
case opISNOTNULL:
if ( !getValue( value1, mLeft, fields, f ) )
return false;
return ( mOp == opISNULL ) == value1.isNull();
case opEQ:
case opNE:
case opGT:
case opLT:
case opGE:
case opLE:
{
if ( !getValue( value1, mLeft, fields, f ) || !getValue( value2, mRight, fields, f ) )
return false;
if ( value1.isNull() || value2.isNull() )
{
// NULL values never match
return false;
}
QgsSearchTreeValue res = QgsSearchTreeValue::compare( value1, value2 );
if ( res.isError() )
{
mError = QString( "%1 [%2]" ).arg( res.string() ).arg( res.number() );
return false;
}
switch ( mOp )
{
case opEQ: return res.number() == 0.0;
case opNE: return res.number() != 0.0;
case opGT: return res.number() > 0.0;
case opLT: return res.number() < 0.0;
case opGE: return res.number() >= 0.0;
case opLE: return res.number() <= 0.0;
default:
mError = QObject::tr( "Unexpected state when evaluating operator!" );
return false;
}
}
break;
case opIN:
case opNOTIN:
{
if ( !getValue( value1, mLeft, fields, f ) ||
!mRight || mRight->type() != tNodeList )
{
return false;
}
foreach( QgsSearchTreeNode * node, mRight->mNodeList )
{
if ( !getValue( value2, node, fields, f ) )
{
mError = QObject::tr( "Could not retrieve value of list value" );
return false;
}
QgsSearchTreeValue res = QgsSearchTreeValue::compare( value1, value2 );
if ( res.isNumeric() && res.number() == 0.0 )
{
// found
return mOp == opIN;
}
}
return mOp == opNOTIN;
}
case opRegexp:
case opLike:
case opILike:
{
if ( !getValue( value1, mLeft, fields, f ) ||
!getValue( value2, mRight, fields, f ) )
return false;
// value1 is string to be matched
// value2 is regular expression
// XXX does it make sense to use regexp on numbers?
// in what format should they be?
if ( value1.isNumeric() || value2.isNumeric() )
{
mError = QObject::tr( "Regular expressions on numeric values don't make sense. Use comparison instead." );
return false;
}
// TODO: reuse QRegExp
QString str = value2.string();
if ( mOp == opLike || mOp == opILike ) // change from LIKE syntax to regexp
{
// XXX escape % and _ ???
str.replace( "%", ".*" );
str.replace( "_", "." );
return QRegExp( str, mOp == opLike ? Qt::CaseSensitive : Qt::CaseInsensitive ).exactMatch( value1.string() );
}
else
{
return QRegExp( str ).indexIn( value1.string() ) != -1;
}
}
default:
mError = QObject::tr( "Unknown operator: %1" ).arg( mOp );
}
return false;
}
bool QgsSearchTreeNode::getValue( QgsSearchTreeValue& value,
QgsSearchTreeNode* node,
const QgsFieldMap &fields,
const QgsAttributeMap &attributes,
QgsGeometry* geom )
{
QgsFeature f;
f.setAttributeMap( attributes );
if ( geom )
f.setGeometry( *geom );
return getValue( value, node, fields, f );
}
bool QgsSearchTreeNode::getValue( QgsSearchTreeValue& value,
QgsSearchTreeNode* node,
const QgsFieldMap& fields,
QgsFeature &f )
{
value = node->valueAgainst( fields, f );
if ( value.isError() )
{
switch (( int ) value.number() )
{
case 1:
mError = QObject::tr( "Referenced column wasn't found: %1" ).arg( value.string() );
break;
case 2:
mError = QObject::tr( "Division by zero." );
break;
case 3:
mError = QObject::tr( "Unknown operator: %1" ).arg( value.string() );
break;
case 4:
mError = QObject::tr( "Unknown token: %1" ).arg( value.string() );
break;
case 5:
mError = QObject::tr( "Expression error: %1" ).arg( value.string() );
break;
default:
mError = QObject::tr( "Unknown error %1: %2" ).arg( value.number() ).arg( value.string() );
break;
}
return false;
}
return true;
}
QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields,
const QgsAttributeMap &attributes,
QgsGeometry* geom )
{
QgsFeature f;
f.setAttributeMap( attributes );
if ( geom )
f.setGeometry( *geom );
return valueAgainst( fields, f );
}
QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, QgsFeature &f )
{
QgsDebugMsgLevel( "valueAgainst: " + makeSearchString(), 2 );
switch ( mType )
{
case tNumber:
QgsDebugMsgLevel( "number: " + QString::number( mNumber ), 2 );
return QgsSearchTreeValue( mNumber );
case tString:
QgsDebugMsgLevel( "text: " + EVAL_STR( mText ), 2 );
return QgsSearchTreeValue( mText );
case tColumnRef:
{
QgsDebugMsgLevel( "column (" + mText.toLower() + "): ", 2 );
// find field index for the column
QgsFieldMap::const_iterator it;
for ( it = fields.begin(); it != fields.end(); it++ )
{
if ( QString::compare( it->name(), mText, Qt::CaseInsensitive ) == 0 )
break;
}
if ( it == fields.end() )
{
// report missing column if not found
QgsDebugMsgLevel( "ERROR!", 2 );
return QgsSearchTreeValue( 1, mText );
}
// get the value
QVariant val = f.attributeMap()[it.key()];
if ( val.isNull() )
{
QgsDebugMsgLevel( " NULL", 2 );
return QgsSearchTreeValue();
}
else if ( val.type() == QVariant::Bool || val.type() == QVariant::Int || val.type() == QVariant::Double )
{
QgsDebugMsgLevel( " number: " + QString::number( val.toDouble() ), 2 );
return QgsSearchTreeValue( val.toDouble() );
}
else
{
QgsDebugMsgLevel( " text: " + EVAL_STR( val.toString() ), 2 );
return QgsSearchTreeValue( val.toString() );
}
}
// arithmetic operators
case tOperator:
{
QgsSearchTreeValue value1, value2, value3;
if ( mLeft )
{
if ( mLeft->type() != tNodeList )
{
if ( !getValue( value1, mLeft, fields, f ) )
return value1;
}
else
{
if ( mLeft->mNodeList.size() > 0 && !getValue( value1, mLeft->mNodeList[0], fields, f ) )
return value1;
if ( mLeft->mNodeList.size() > 1 && !getValue( value2, mLeft->mNodeList[1], fields, f ) )
return value2;
if ( mLeft->mNodeList.size() > 2 && !getValue( value3, mLeft->mNodeList[2], fields, f ) )
return value3;
}
}
if ( mRight )
{
Q_ASSERT( !mLeft || mLeft->type() != tNodeList );
if ( !getValue( value2, mRight, fields, f ) )
return value2;
}
if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY || mOp == opXAT || mOp == opYAT )
{
if ( !f.geometry() )
{
return QgsSearchTreeValue( 2, QObject::tr( "Geometry is 0" ) );
}
//check that we don't use area for lines or length for polygons
if ( mOp == opLENGTH && f.geometry()->type() == QGis::Line )
{
return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
}
if ( mOp == opAREA && f.geometry()->type() == QGis::Polygon )
{
return QgsSearchTreeValue( mCalc->measure( f.geometry() ) );
}
if ( mOp == opPERIMETER && f.geometry()->type() == QGis::Polygon )
{
return QgsSearchTreeValue( mCalc->measurePerimeter( f.geometry() ) );
}
if ( mOp == opX && f.geometry()->type() == QGis::Point )
{
return QgsSearchTreeValue( f.geometry()->asPoint().x() );
}
if ( mOp == opY && f.geometry()->type() == QGis::Point )
{
return QgsSearchTreeValue( f.geometry()->asPoint().y() );
}
if (( mOp == opXAT || mOp == opYAT ) && f.geometry()->type() == QGis::Line && value1.isNumeric() )
{
QgsPolyline p = f.geometry()->asPolyline();
int idx = value1.number();
if ( idx < 0 )
{
idx += p.size();
}
if ( idx < 0 || idx >= p.size() )
{
return QgsSearchTreeValue( 2, QObject::tr( "Index %1 out of range [0;%2[" ).arg( idx ).arg( p.size() ) );
}
return QgsSearchTreeValue( mOp == opXAT ? p[idx].x() : p[idx].y() );
}
return QgsSearchTreeValue( 0 );
}
if ( mOp == opID )
{
return QgsSearchTreeValue( f.id() );
}
if ( mOp == opROWNUM )
{
// the row number has to be previously set by the caller using setCurrentRowNumber
return QgsSearchTreeValue( mNumber );
}
//string operations with one argument
if ( !mRight && !value1.isNumeric() )
{
if ( mOp == opTOINT )
{
return QgsSearchTreeValue( value1.string().toInt() );
}
else if ( mOp == opTOREAL )
{
return QgsSearchTreeValue( value1.string().toDouble() );
}
}
//don't convert to numbers in case of string concatenation
if ( mLeft && mRight && !value1.isNumeric() && !value2.isNumeric() )
{
// TODO: concatenation using '+' operator should be removed in favor of '||' operator
// because it may lead to surprising behavior if numbers are stored in a string
if ( mOp == opPLUS )
{
return QgsSearchTreeValue( value1.string() + value2.string() );
}
}
// string concatenation ||
if ( mLeft && mRight && mOp == opCONCAT )
{
if ( value1.isNumeric() && value2.isNumeric() )
{
return QgsSearchTreeValue( 5, QObject::tr( "Operator doesn't match the argument types." ) );
}
else
{
QString arg1 = value1.isNumeric() ? QString::number( value1.number() ) : value1.string();
QString arg2 = value2.isNumeric() ? QString::number( value2.number() ) : value2.string();
return QgsSearchTreeValue( arg1 + arg2 );
}
}
// string operations
switch ( mOp )
{
case opLOWER:
return QgsSearchTreeValue( value1.string().toLower() );
case opUPPER:
return QgsSearchTreeValue( value1.string().toUpper() );
case opSTRLEN:
return QgsSearchTreeValue( value1.string().length() );
case opREPLACE:
return QgsSearchTreeValue( value1.string().replace( value2.string(), value3.string() ) );
case opREGEXPREPLACE:
{
QRegExp re( value2.string() );
if ( !re.isValid() )
{
return QgsSearchTreeValue( 5, QObject::tr( "Invalid regular expression '%1': %2" ).arg( value2.string() ).arg( re.errorString() ) );
}
return QgsSearchTreeValue( value1.string().replace( re, value3.string() ) );
}
case opSUBSTR:
return QgsSearchTreeValue( value1.string().mid( value2.number() - 1, value3.number() ) );
default:
break;
}
// for other operators, convert strings to numbers if needed
double val1, val2;
if ( value1.isNumeric() )
val1 = value1.number();
else
val1 = value1.string().toDouble();
if ( value2.isNumeric() )
val2 = value2.number();
else
val2 = value2.string().toDouble();
switch ( mOp )
{
case opPLUS:
return QgsSearchTreeValue( val1 + val2 );
case opMINUS:
return QgsSearchTreeValue( val1 - val2 );
case opMUL:
return QgsSearchTreeValue( val1 * val2 );
case opMOD:
// NOTE: we _might_ support float operators, like postgresql does
// see 83c94a886c059 commit in postgresql git repo for more info
return QgsSearchTreeValue( int( val1 ) % int( val2 ) );
case opDIV:
if ( val2 == 0 )
return QgsSearchTreeValue( 2, "" ); // division by zero
else
return QgsSearchTreeValue( val1 / val2 );
case opPOW:
if (( val1 == 0 && val2 < 0 ) || ( val2 < 0 && ( val2 - floor( val2 ) ) > 0 ) )
{
return QgsSearchTreeValue( 4, QObject::tr( "Error in power function" ) );
}
return QgsSearchTreeValue( pow( val1, val2 ) );
case opSQRT:
return QgsSearchTreeValue( sqrt( val1 ) );
case opSIN:
return QgsSearchTreeValue( sin( val1 ) );
case opCOS:
return QgsSearchTreeValue( cos( val1 ) );
case opTAN:
return QgsSearchTreeValue( tan( val1 ) );
case opASIN:
return QgsSearchTreeValue( asin( val1 ) );
case opACOS:
return QgsSearchTreeValue( acos( val1 ) );
case opATAN:
return QgsSearchTreeValue( atan( val1 ) );
case opATAN2:
return QgsSearchTreeValue( atan2( val1, val2 ) );
case opTOINT:
return QgsSearchTreeValue( int( val1 ) );
case opTOREAL:
return QgsSearchTreeValue( val1 );
case opTOSTRING:
return QgsSearchTreeValue( QString::number( val1 ) );
default:
return QgsSearchTreeValue( 3, QString::number( mOp ) ); // unknown operator
}
}
default:
return QgsSearchTreeValue( 4, QString::number( mType ) ); // unknown token
}
}
void QgsSearchTreeNode::setCurrentRowNumber( int rownum )
{
if ( mType == tOperator )
{
if ( mOp == opROWNUM )
mNumber = rownum;
else
{
// propagate the new row number to children
if ( mLeft )
mLeft->setCurrentRowNumber( rownum );
if ( mRight )
mRight->setCurrentRowNumber( rownum );
}
}
}
void QgsSearchTreeNode::append( QgsSearchTreeNode *node )
{
Q_ASSERT( mType == tNodeList );
mNodeList.append( node );
}
void QgsSearchTreeNode::append( QList<QgsSearchTreeNode *> nodes )
{
foreach( QgsSearchTreeNode * node, nodes )
{
mNodeList.append( node );
}
}
QgsSearchTreeValue QgsSearchTreeValue::compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2, Qt::CaseSensitivity cs )
{
if ( value1.isNumeric() || value2.isNumeric() )
{
// numeric comparison
// convert to numbers if needed
double val1, val2;
bool ok;
if ( value1.isNumeric() )
{
val1 = value1.number();
}
else
{
val1 = value1.string().toDouble( &ok );
if ( !ok )
{
return QgsSearchTreeValue( 5, QObject::tr( "Value '%1' is not numeric" ).arg( value1.string() ) );
}
}
if ( value2.isNumeric() )
{
val2 = value2.number();
}
else
{
val2 = value2.string().toDouble( &ok );
if ( !ok )
{
return QgsSearchTreeValue( 5, QObject::tr( "Value '%1' is not numeric" ).arg( value2.string() ) );
}
}
QgsDebugMsgLevel( "NUM_COMP: " + QString::number( val1 ) + " ~ " + QString::number( val2 ), 2 );
if ( val1 < val2 )
return QgsSearchTreeValue( -1.0 );
else if ( val1 > val2 )
return QgsSearchTreeValue( 1.0 );
else
return QgsSearchTreeValue( 0.0 );
}
else
{
// string comparison
return QgsSearchTreeValue(( double ) value1.string().compare( value2.string(), cs ) );
}
}

View File

@ -1,292 +0,0 @@
/***************************************************************************
qgssearchtreenode.h
Definition of node for parsed tree of search string
--------------------
begin : 2005-07-26
copyright : (C) 2005 by Martin Dobias
email : won.der at centrum.sk
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSEARCHTREENODE_H
#define QGSSEARCHTREENODE_H
#include <QMap>
#include <QString>
#include <QStringList>
#include <QVariant>
#include <QList>
#include <qgis.h>
#include <qgsfield.h>
#include <qgsfeature.h>
class QgsDistanceArea;
class QgsSearchTreeValue;
/** \ingroup core
* A representation of a node in a search tree.
*
* node in tree of parsed search string
* node is terminal (has no children) if it's a number, column ref or string
* non-terminal is only node with operator - with 1 or 2 children
*/
class CORE_EXPORT QgsSearchTreeNode
{
public:
//! defines possible types of node
enum Type
{
tOperator = 1,
tNumber,
tColumnRef,
tString,
tNodeList,
};
//! possible operators
//! TODO: sync the python bindings
enum Operator
{
// binary
opAND = 1,
opOR,
opNOT,
// arithmetic
opPLUS,
opMINUS,
opMUL,
opMOD,
opDIV,
opPOW,
opSQRT,
opSIN,
opCOS,
opTAN,
opASIN,
opACOS,
opATAN,
opATAN2,
// conversion
opTOINT,
opTOREAL,
opTOSTRING,
// coordinates
opX,
opY,
opXAT,
opYAT,
// measuring
opLENGTH,
opAREA,
opPERIMETER,
// feature id
opID,
// comparison
opISNULL, // IS NULL
opISNOTNULL, // IS NOT NULL
opEQ, // =
opNE, // != resp. <>
opGT, // >
opLT, // <
opGE, // >=
opLE, // <=
opRegexp, // ~
opLike, // LIKE
opILike, // ILIKE
opIN, // IN
opNOTIN, // NOT IN
// string handling
opCONCAT,
opLOWER,
opUPPER,
opREPLACE,
opREGEXPREPLACE,
opSTRLEN,
opSUBSTR,
opROWNUM
};
//! constructors
QgsSearchTreeNode( Type type );
QgsSearchTreeNode( double number );
QgsSearchTreeNode( Operator op, QgsSearchTreeNode* left, QgsSearchTreeNode* right );
QgsSearchTreeNode( QString text, bool isColumnRef );
//! copy contructor - copies whole tree!
QgsSearchTreeNode( const QgsSearchTreeNode& node );
//! destructor - deletes children nodes (if any)
~QgsSearchTreeNode();
//! returns type of current node
Type type() const { return mType; }
//! node value getters
Operator op() const { return mOp; }
double number() const { return mNumber; }
QString columnRef() const { return mText; }
QString string() const { return mText; }
//! node value setters (type is set also)
void setOp( Operator op ) { mType = tOperator; mOp = op; }
void setNumber( double number ) { mType = tNumber; mNumber = number; }
void setColumnRef( const QString& str ) { mType = tColumnRef; mText = str; }
void setString( const QString& str ) { mType = tString; mText = str; stripText(); }
//! children
QgsSearchTreeNode* Left() { return mLeft; }
QgsSearchTreeNode* Right() { return mRight; }
void setLeft( QgsSearchTreeNode* left ) { mLeft = left; }
void setRight( QgsSearchTreeNode* right ) { mRight = right; }
//! returns search string that should be equal to original parsed string
QString makeSearchString();
//! checks whether the node tree is valid against supplied attributes
//! @note attribute and optional geom parameter replaced with feature in 1.6
bool checkAgainst( const QgsFieldMap& fields, QgsFeature &f );
//! @deprecated
Q_DECL_DEPRECATED bool checkAgainst( const QgsFieldMap& fields, const QgsAttributeMap& attributes, QgsGeometry* geom = 0 );
//! checks if there were errors during evaluation
bool hasError() { return ( !mError.isEmpty() ); }
//! returns error message
const QString& errorMsg() { return mError; }
//! wrapper around valueAgainst()
//! @note added in 1.4
bool getValue( QgsSearchTreeValue& value,
QgsSearchTreeNode* node,
const QgsFieldMap& fields,
QgsFeature &f );
//! @deprecated
Q_DECL_DEPRECATED bool getValue( QgsSearchTreeValue& value,
QgsSearchTreeNode* node,
const QgsFieldMap &fields,
const QgsAttributeMap &attributes,
QgsGeometry* geom = 0 );
//! return a list of referenced columns in the tree
//! @note added in 1.5
QStringList referencedColumns();
//! return a list of all attribute nodes
//! @note added in 1.5
QList<QgsSearchTreeNode*> columnRefNodes();
//! check whether there are any operators that need geometry (for area, length)
//! @note added in 1.5
bool needsGeometry();
//! return quoted column reference (in double quotes)
//! @note added in 1.5
static QString quotedColumnRef( QString name );
//! Set current row number within this tree.
//! This value is stored only in the nodes being $rownum operator - in mNumber
//! @note added in 1.6
void setCurrentRowNumber( int rownum );
//! append a node to the list
//! @note added in 1.6
void append( QgsSearchTreeNode * );
//! append nodelist to the list
//! @note added in 1.6
void append( QList<QgsSearchTreeNode*> );
protected:
//! returns scalar value of node
QgsSearchTreeValue valueAgainst( const QgsFieldMap& fields, QgsFeature &f );
//! @deprecated
Q_DECL_DEPRECATED QgsSearchTreeValue valueAgainst( const QgsFieldMap& fields, const QgsAttributeMap& attributes, QgsGeometry* geom = 0 );
//! strips mText when node is of string type
void stripText();
//! strip mText when column reference is quoted
void stripColRef();
//! initialize node's internals
void init();
private:
//! node type
Type mType;
//! data
Operator mOp;
double mNumber;
QString mText;
QList<QgsSearchTreeNode *> mNodeList;
QString mError;
//! children
QgsSearchTreeNode* mLeft;
QgsSearchTreeNode* mRight;
/**For length() and area() functions*/
QgsDistanceArea* mCalc;
};
// TODO: put it into separate file
class CORE_EXPORT QgsSearchTreeValue
{
public:
enum Type
{
valError,
valString,
valNumber,
valNull
};
QgsSearchTreeValue() { mType = valNull; }
QgsSearchTreeValue( QString string ) { mType = string.isNull() ? valNull : valString; mString = string; }
QgsSearchTreeValue( double number ) { mType = valNumber; mNumber = number; }
QgsSearchTreeValue( int error, QString errorMsg ) { mType = valError; mNumber = error; mString = errorMsg; }
static QgsSearchTreeValue compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2,
Qt::CaseSensitivity = Qt::CaseSensitive );
bool isNumeric() { return mType == valNumber; }
bool isError() { return mType == valError; }
bool isNull() { return mType == valNull; }
QString& string() { return mString; }
double number() { return mNumber; }
private:
Type mType;
QString mString;
double mNumber;
};
#endif

View File

@ -92,7 +92,6 @@ ADD_QGIS_TEST(geometrytest testqgsgeometry.cpp)
ADD_QGIS_TEST(coordinatereferencesystemtest testqgscoordinatereferencesystem.cpp)
ADD_DEPENDENCIES(qgis_coordinatereferencesystemtest synccrsdb)
ADD_QGIS_TEST(pointtest testqgspoint.cpp)
ADD_QGIS_TEST(searchstringtest testqgssearchstring.cpp)
ADD_QGIS_TEST(vectorlayertest testqgsvectorlayer.cpp)
ADD_QGIS_TEST(rulebasedrenderertest testqgsrulebasedrenderer.cpp)
ADD_QGIS_TEST(ziplayertest testziplayer.cpp)

View File

@ -1,74 +0,0 @@
/***************************************************************************
testqgssearchstring.cpp
--------------------------------------
Date : March 28, 2010
Copyright : (C) 2010 Martin Dobias
Email : wonder.sk at gmail.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QtTest>
#include <qgssearchstring.h>
#include <qgssearchtreenode.h>
#include <qgsfeature.h>
class TestQgsSearchString : public QObject
{
Q_OBJECT;
private slots:
//void initTestCase();// will be called before the first testfunction is executed.
//void cleanupTestCase();// will be called after the last testfunction was executed.
//void init();// will be called before each testfunction is executed.
//void cleanup();// will be called after every testfunction.
void testLike();
void testRegexp();
private:
QString mReport;
};
static bool evalString( QString str )
{
QgsFeature f;
QgsSearchString ss;
ss.setString( str );
return ss.tree()->checkAgainst( QgsFieldMap(), f );
}
void TestQgsSearchString::testLike()
{
QVERIFY( evalString( "'a' LIKE 'a'" ) );
QVERIFY( ! evalString( "'aa' LIKE 'a'" ) );
QVERIFY( ! evalString( "'a' LIKE 'b'" ) );
QVERIFY( evalString( "'abba' LIKE 'a%'" ) );
QVERIFY( ! evalString( "'abba' LIKE 'b%'" ) );
QVERIFY( evalString( "'abba' LIKE '%a'" ) );
QVERIFY( ! evalString( "'abba' LIKE '%b'" ) );
QVERIFY( evalString( "'abba' LIKE '%bb%'" ) );
QVERIFY( evalString( "'abba' LIKE 'a%a'" ) );
QVERIFY( ! evalString( "'abba' LIKE 'b%b'" ) );
}
void TestQgsSearchString::testRegexp()
{
QVERIFY( evalString( "'a' ~ 'a'" ) );
QVERIFY( ! evalString( "'b' ~ 'a'" ) );
QVERIFY( evalString( "'abba' ~ 'a'" ) );
QVERIFY( ! evalString( "'abba' ~ 'aba'" ) );
QVERIFY( evalString( "'abba' ~ 'a.*a'" ) );
QVERIFY( evalString( "'abba' ~ 'a[b]+a'" ) );
}
QTEST_MAIN( TestQgsSearchString )
#include "moc_testqgssearchstring.cxx"