mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
[API] removed QgsSearchString - QgsExpression should be used instead
This commit is contained in:
parent
a0a0e04a39
commit
e35b115881
@ -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
|
||||
|
@ -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();
|
||||
|
||||
};
|
@ -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();
|
||||
|
||||
};
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 ) );
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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"
|
Loading…
x
Reference in New Issue
Block a user