Skip to content

Commit

Permalink
[FEATURE] search string update
Browse files Browse the repository at this point in the history
- added regexp_replace operator
- check types on comparison (QgsSearchTreeValue::compare now returns QgsSearchTreeValue for error reporting)
- field calculator shows expression errors.
- update node types and operators in sip bindings
  • Loading branch information
jef-n committed Jul 20, 2011
1 parent 7d74fe0 commit 6c26773
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 37 deletions.
43 changes: 32 additions & 11 deletions python/core/qgssearchtreenode.sip
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class QgsSearchTreeNode
tOperator = 1,
tNumber,
tColumnRef,
tString
tString,
tNodeList,
};

//! possible operators
Expand All @@ -27,6 +28,7 @@ class QgsSearchTreeNode
opPLUS,
opMINUS,
opMUL,
opMOD,
opDIV,
opPOW,
opSQRT,
Expand All @@ -36,31 +38,50 @@ class QgsSearchTreeNode
opASIN,
opACOS,
opATAN,
opATAN2,

// conversion
opTOINT,
opTOREAL,
opTOSTRING,

// coordinates
opX,
opY,
opXAT,
opYAT,

// measuring
opLENGTH,
opAREA,
opPERIMETER,

// feature id
opID,

// comparison
opEQ, // =
opNE, // != resp. <>
opGT, // >
opLT, // <
opGE, // >=
opLE, // <=
opRegexp, // ~
opLike, // LIKE
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
};
Expand Down Expand Up @@ -177,8 +198,8 @@ class QgsSearchTreeValue
QgsSearchTreeValue( double number );
QgsSearchTreeValue( int error, QString errorMsg );

static int compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2,
Qt::CaseSensitivity = Qt::CaseSensitive );
static QgsSearchTreeValue compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2,
Qt::CaseSensitivity = Qt::CaseSensitive );

bool isNumeric();
bool isError();
Expand Down
3 changes: 2 additions & 1 deletion resources/context_help/QgsFieldCalculator-de_DE
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ Der Feldrechner erlaubt Ihnen Felder mit Ausdrücken zu setzen.
<tr><td>upper(<tt>a</tt>)</td><td>Zeichenkette <tt>a</tt> in Gro&szlig;buchstaben umwandeln</td></tr>
<tr><td>length(<tt>a</tt>)</td><td>L&auml;nge der Zeichenkette <tt>a</tt></td></tr>
<tr><td>atan2(y,x)</td><td>Arcustangens von y/x mit Vorzeichen der beiden Argumenten, um den Quadranten des Ergebnisses zu bestimmen.</td></tr>
<tr><td>replace(<tt>a</tt>,<i>ersetzedieses</i>,<i>durchjenes</i>)</td><td>In der Zeichenkette <tt>a</tt> <i>ersetzedieses</i> durch <i>durchjenes</i> ersetzen.</td></td>
<tr><td>replace(<tt>a</tt>,<i>streiche</i>,<i>setze</i>)</td><td>In der Zeichenkette <tt>a</tt> <i>streiche</i> durch <i>setze</i> ersetzen.</td></td>
<tr><td>regexp_replace(<tt>a</tt>,<i>streiche</i>,<i>setze</i>)</td><td>In der Zeichenkette <tt>a</tt> den regul�ren Ausdruck <i>streiche</i> durch <i>setze</i> ersetzen.</td></td>
<tr><td>substr(<tt>a</tt>,<i>von</i>,<i>l&auml;nge</i>)</td><td><i>l&uuml;nge</i> Zeichen der Zeichenkette <tt>a</tt> ab Stelle <i>von</i> (Das erste Zeichen hat den Index 1)</td></td>
<tr><td><tt>a</tt> || <tt>b</tt></td><td>Zeichenkette <tt>a</tt> and <tt>b</tt> zusammenziehen</td></tr>
<tr><td>$rownum</td><td>Aktuelle Zeilennummer</td></tr>
Expand Down
1 change: 1 addition & 0 deletions resources/context_help/QgsFieldCalculator-en_US
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ The field calculator allows you to update fields with expressions.
<tr><td>length(<tt>a</tt>)</td><td>length of string <tt>a</tt></td></tr>
<tr><td>atan2(y,x)</td><td>arcustangens of y/x using the signs of the two arguments to determine the quadrant of the result.</td></tr>
<tr><td>replace(<tt>a</tt>,<i>replacethis</i>,<i>withthat</i>)</td><td>replace <i>replacethis</i> with <i>withthat</i> in string <tt>a</tt></td></td>
<tr><td>regexp_replace(<tt>a</tt>,<i>replacethis</i>,<i>withthat</i>)</td><td>replace the regular expression <i>replacethis</i> with <i>withthat</i> in string <tt>a</tt></td></td>
<tr><td>substr(<tt>a</tt>,from,len)</td><td>len characters of string <tt>a</tt> starting from from (first character index is 1)</td></td>
<tr><td><tt>a</tt> || <tt>b</tt></td><td>concatenate strings <tt>a</tt> and <tt>b</tt></td></tr>
<tr><td>$rownum</td><td>number current row</td></tr>
Expand Down
4 changes: 3 additions & 1 deletion src/app/qgsfieldcalculator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ void QgsFieldCalculator::accept()
//go through all the features and change the new attribute
QgsFeature feature;
bool calculationSuccess = true;
QString error;

bool onlySelected = ( mOnlyUpdateSelectedCheckBox->checkState() == Qt::Checked );
QgsFeatureIds selectedIds = mVectorLayer->selectedFeaturesIds();
Expand Down Expand Up @@ -183,6 +184,7 @@ void QgsFieldCalculator::accept()
else
{
calculationSuccess = false;
error = searchTree->errorMsg();
break;
}
}
Expand Down Expand Up @@ -225,7 +227,7 @@ void QgsFieldCalculator::accept()

if ( !calculationSuccess )
{
QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string." ) );
QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string:\n%1" ).arg( error ) );
mVectorLayer->destroyEditCommand();
return;
}
Expand Down
1 change: 1 addition & 0 deletions src/core/qgssearchstringlexer.ll
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ string "'"{str_char}*"'"
"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; }
Expand Down
80 changes: 58 additions & 22 deletions src/core/qgssearchtreenode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ QString QgsSearchTreeNode::makeSearchString()
mOp == opASIN || mOp == opACOS || mOp == opATAN ||
mOp == opTOINT || mOp == opTOREAL || mOp == opTOSTRING ||
mOp == opLOWER || mOp == opUPPER || mOp == opSTRLEN ||
mOp == opATAN2 || mOp == opREPLACE || mOp == opSUBSTR ||
mOp == opXAT || mOp == opYAT )
mOp == opATAN2 || mOp == opREPLACE || mOp == opREGEXPREPLACE ||
mOp == opSUBSTR || mOp == opXAT || mOp == opYAT )
{
// functions
switch ( mOp )
Expand All @@ -226,6 +226,7 @@ QString QgsSearchTreeNode::makeSearchString()
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;
Expand Down Expand Up @@ -407,7 +408,6 @@ bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, QgsFeature &f )
}

QgsSearchTreeValue value1, value2;
int res;

switch ( mOp )
{
Expand Down Expand Up @@ -437,6 +437,7 @@ bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, QgsFeature &f )
case opLT:
case opGE:
case opLE:
{
if ( !getValue( value1, mLeft, fields, f ) || !getValue( value2, mRight, fields, f ) )
return false;

Expand All @@ -446,20 +447,27 @@ bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, QgsFeature &f )
return false;
}

res = QgsSearchTreeValue::compare( value1, value2 );
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 == 0;
case opNE: return res != 0;
case opGT: return res > 0;
case opLT: return res < 0;
case opGE: return res >= 0;
case opLE: return res <= 0;
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:
Expand All @@ -478,9 +486,9 @@ bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, QgsFeature &f )
return false;
}

res = QgsSearchTreeValue::compare( value1, value2 );
QgsSearchTreeValue res = QgsSearchTreeValue::compare( value1, value2 );

if ( res == 0 )
if ( res.isNumeric() && res.number() == 0.0 )
{
// found
return mOp == opIN;
Expand Down Expand Up @@ -561,16 +569,17 @@ bool QgsSearchTreeNode::getValue( QgsSearchTreeValue& value,
case 2:
mError = QObject::tr( "Division by zero." );
break;

// these should never happen (no need to translate)
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!" );
mError = QObject::tr( "Unknown error %1: %2" ).arg( value.number() ).arg( value.string() );
break;
}
return false;
Expand Down Expand Up @@ -779,6 +788,16 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q
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:
Expand Down Expand Up @@ -884,35 +903,52 @@ void QgsSearchTreeNode::append( QList<QgsSearchTreeNode *> nodes )
}
}

int QgsSearchTreeValue::compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2, Qt::CaseSensitivity cs )
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();
{
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();
{
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 -1;
return QgsSearchTreeValue( -1.0 );
else if ( val1 > val2 )
return 1;
return QgsSearchTreeValue( 1.0 );
else
return 0;
return QgsSearchTreeValue( 0.0 );
}
else
{
// string comparison
return value1.string().compare( value2.string(), cs );
return QgsSearchTreeValue(( double ) value1.string().compare( value2.string(), cs ) );
}
}
6 changes: 4 additions & 2 deletions src/core/qgssearchtreenode.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class CORE_EXPORT QgsSearchTreeNode
};

//! possible operators
//! TODO: sync the python bindings
enum Operator
{
// binary
Expand Down Expand Up @@ -116,6 +117,7 @@ class CORE_EXPORT QgsSearchTreeNode
opLOWER,
opUPPER,
opREPLACE,
opREGEXPREPLACE,
opSTRLEN,
opSUBSTR,

Expand Down Expand Up @@ -269,8 +271,8 @@ class CORE_EXPORT QgsSearchTreeValue
QgsSearchTreeValue( double number ) { mType = valNumber; mNumber = number; }
QgsSearchTreeValue( int error, QString errorMsg ) { mType = valError; mNumber = error; mString = errorMsg; }

static int compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2,
Qt::CaseSensitivity = Qt::CaseSensitive );
static QgsSearchTreeValue compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2,
Qt::CaseSensitivity = Qt::CaseSensitive );

bool isNumeric() { return mType == valNumber; }
bool isError() { return mType == valError; }
Expand Down

0 comments on commit 6c26773

Please sign in to comment.