Skip to content

Commit 1d6e5d2

Browse files
committedOct 4, 2016
[BUGFIX] Support OGC PropertyIsLike attributs
The OGC PropertyIsLike element can have 4 attributs: * matchCase to specify LIKE or ILIKE * wildCard to specify a wildcard char symbol * signleChar to specify a single char symbol * escape to specify an escape char symbol
1 parent b47f03d commit 1d6e5d2

File tree

2 files changed

+123
-10
lines changed

2 files changed

+123
-10
lines changed
 

‎src/core/qgsogcutils.cpp

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,6 +1666,10 @@ static int binaryOperatorFromTagName( const QString& tagName )
16661666

16671667
static QString binaryOperatorToTagName( QgsExpression::BinaryOperator op )
16681668
{
1669+
if ( op == QgsExpression::boILike )
1670+
{
1671+
return "PropertyIsLike";
1672+
}
16691673
return binaryOperatorsTagNamesMap().key( op, QString() );
16701674
}
16711675

@@ -1752,6 +1756,11 @@ QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter(
17521756
return nullptr;
17531757
}
17541758

1759+
if ( op == QgsExpression::boLike && element.hasAttribute( "matchCase" ) && element.attribute( "matchCase" ) == "false" )
1760+
{
1761+
op = QgsExpression::boILike;
1762+
}
1763+
17551764
QDomElement operandElem = element.firstChildElement();
17561765
QgsExpression::Node *expr = nodeFromOgcFilter( operandElem, errorMessage ), *leftOp = expr;
17571766
if ( !expr )
@@ -1772,6 +1781,64 @@ QgsExpression::NodeBinaryOperator* QgsOgcUtils::nodeBinaryOperatorFromOgcFilter(
17721781
return nullptr;
17731782
}
17741783

1784+
if ( op == QgsExpression::boLike || op == QgsExpression::boILike )
1785+
{
1786+
QString wildCard;
1787+
if ( element.hasAttribute( "wildCard" ) )
1788+
{
1789+
wildCard = element.attribute( "wildCard" );
1790+
}
1791+
QString singleChar;
1792+
if ( element.hasAttribute( "singleChar" ) )
1793+
{
1794+
singleChar = element.attribute( "singleChar" );
1795+
}
1796+
QString escape = "\\";
1797+
if ( element.hasAttribute( "escape" ) )
1798+
{
1799+
escape = element.attribute( "escape" );
1800+
}
1801+
// replace
1802+
QString oprValue = static_cast<const QgsExpression::NodeLiteral*>( opRight )->value().toString();
1803+
if ( !wildCard.isEmpty() && wildCard != "%" )
1804+
{
1805+
oprValue.replace( '%', "\\%" );
1806+
if ( oprValue.startsWith( wildCard ) )
1807+
{
1808+
oprValue.replace( 0, 1, "%" );
1809+
}
1810+
QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( wildCard ) + ")" );
1811+
int pos = 0;
1812+
while (( pos = rx.indexIn( oprValue, pos ) ) != -1 )
1813+
{
1814+
oprValue.replace( pos + 1, 1, "%" );
1815+
pos += 1;
1816+
}
1817+
oprValue.replace( escape + wildCard, wildCard );
1818+
}
1819+
if ( !singleChar.isEmpty() && singleChar != "_" )
1820+
{
1821+
oprValue.replace( '_', "\\_" );
1822+
if ( oprValue.startsWith( singleChar ) )
1823+
{
1824+
oprValue.replace( 0, 1, "_" );
1825+
}
1826+
QRegExp rx( "[^" + QRegExp::escape( escape ) + "](" + QRegExp::escape( singleChar ) + ")" );
1827+
int pos = 0;
1828+
while (( pos = rx.indexIn( oprValue, pos ) ) != -1 )
1829+
{
1830+
oprValue.replace( pos + 1, 1, "_" );
1831+
pos += 1;
1832+
}
1833+
oprValue.replace( escape + singleChar, singleChar );
1834+
}
1835+
if ( !escape.isEmpty() && escape != "\\" )
1836+
{
1837+
oprValue.replace( escape + escape, escape );
1838+
}
1839+
opRight = new QgsExpression::NodeLiteral( oprValue );
1840+
}
1841+
17751842
expr = new QgsExpression::NodeBinaryOperator( static_cast< QgsExpression::BinaryOperator >( op ), expr, opRight );
17761843
}
17771844

@@ -2289,13 +2356,13 @@ QDomElement QgsOgcUtilsExprToFilter::expressionBinaryOperatorToOgcFilter( const
22892356
if ( op == QgsExpression::boILike )
22902357
boElem.setAttribute( "matchCase", "false" );
22912358

2292-
// setup wildcards to <ogc:PropertyIsLike>
2359+
// setup wildCards to <ogc:PropertyIsLike>
22932360
boElem.setAttribute( "wildCard", "%" );
2294-
boElem.setAttribute( "singleChar", "?" );
2361+
boElem.setAttribute( "singleChar", "_" );
22952362
if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2296-
boElem.setAttribute( "escape", "!" );
2363+
boElem.setAttribute( "escape", "\\" );
22972364
else
2298-
boElem.setAttribute( "escapeChar", "!" );
2365+
boElem.setAttribute( "escapeChar", "\\" );
22992366
}
23002367

23012368
boElem.appendChild( leftElem );
@@ -2712,6 +2779,8 @@ QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement:
27122779
opText = "PropertyIsGreaterThan";
27132780
else if ( op == QgsSQLStatement::boLike )
27142781
opText = "PropertyIsLike";
2782+
else if ( op == QgsSQLStatement::boILike )
2783+
opText = "PropertyIsLike";
27152784

27162785
if ( opText.isEmpty() )
27172786
{
@@ -2727,13 +2796,13 @@ QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement:
27272796
if ( op == QgsSQLStatement::boILike )
27282797
boElem.setAttribute( "matchCase", "false" );
27292798

2730-
// setup wildcards to <ogc:PropertyIsLike>
2799+
// setup wildCards to <ogc:PropertyIsLike>
27312800
boElem.setAttribute( "wildCard", "%" );
2732-
boElem.setAttribute( "singleChar", "?" );
2801+
boElem.setAttribute( "singleChar", "_" );
27332802
if ( mFilterVersion == QgsOgcUtils::FILTER_OGC_1_0 )
2734-
boElem.setAttribute( "escape", "!" );
2803+
boElem.setAttribute( "escape", "\\" );
27352804
else
2736-
boElem.setAttribute( "escapeChar", "!" );
2805+
boElem.setAttribute( "escapeChar", "\\" );
27372806
}
27382807

27392808
boElem.appendChild( leftElem );

‎tests/src/core/testqgsogcutils.cpp

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,41 @@ void TestQgsOgcUtils::testExpressionFromOgcFilter_data()
180180
"</Filter>" )
181181
<< QString( "POPULATION >= 100 AND POPULATION <= 200" );
182182

183-
// TODO: needs to handle different wildcards, single chars, escape chars
183+
// handle different wildcards, single chars, escape chars
184184
QTest::newRow( "like" ) << QString(
185185
"<Filter>"
186-
"<PropertyIsLike wildcard='*' singleChar='.' escape='!'>"
186+
"<PropertyIsLike wildCard=\"%\" singleChar=\"_\" escape=\"\\\">"
187187
"<PropertyName>NAME</PropertyName><Literal>*QGIS*</Literal></PropertyIsLike>"
188188
"</Filter>" )
189189
<< QString( "NAME LIKE '*QGIS*'" );
190+
QTest::newRow( "ilike" ) << QString(
191+
"<Filter>"
192+
"<PropertyIsLike matchCase=\"false\" wildCard=\"%\" singleChar=\"_\" escape=\"\\\">"
193+
"<PropertyName>NAME</PropertyName><Literal>*QGIS*</Literal></PropertyIsLike>"
194+
"</Filter>" )
195+
<< QString( "NAME ILIKE '*QGIS*'" );
196+
197+
// different wildCards
198+
QTest::newRow( "like wildCard" ) << QString(
199+
"<Filter>"
200+
"<PropertyIsLike wildCard='*' singleChar='.' escape=\"\\\">"
201+
"<PropertyName>NAME</PropertyName><Literal>*%QGIS*\\*</Literal></PropertyIsLike>"
202+
"</Filter>" )
203+
<< QString( "NAME LIKE '%\\\\%QGIS%*'" );
204+
// different single chars
205+
QTest::newRow( "like single char" ) << QString(
206+
"<Filter>"
207+
"<PropertyIsLike wildCard='*' singleChar='.' escape=\"\\\">"
208+
"<PropertyName>NAME</PropertyName><Literal>._QGIS.\\.</Literal></PropertyIsLike>"
209+
"</Filter>" )
210+
<< QString( "NAME LIKE '_\\\\_QGIS_.'" );
211+
// different single chars
212+
QTest::newRow( "like escape char" ) << QString(
213+
"<Filter>"
214+
"<PropertyIsLike wildCard=\"*\" singleChar=\".\" escape=\"!\">"
215+
"<PropertyName>NAME</PropertyName><Literal>_QGIS.!.!!%QGIS*!*</Literal></PropertyIsLike>"
216+
"</Filter>" )
217+
<< QString( "NAME LIKE '\\\\_QGIS_.!\\\\%QGIS%*'" );
190218

191219
QTest::newRow( "is null" ) << QString(
192220
"<Filter>"
@@ -301,6 +329,22 @@ void TestQgsOgcUtils::testExpressionToOgcFilter_data()
301329
"</ogc:And>"
302330
"</ogc:Filter>" );
303331

332+
QTest::newRow( "like" ) << QString( "NAME LIKE '*QGIS*'" ) << QString(
333+
"<ogc:Filter xmlns:ogc=\"http://www.opengis.net/ogc\">"
334+
"<ogc:PropertyIsLike singleChar=\"_\" escape=\"\\\" wildCard=\"%\">"
335+
"<ogc:PropertyName>NAME</ogc:PropertyName>"
336+
"<ogc:Literal>*QGIS*</ogc:Literal>"
337+
"</ogc:PropertyIsLike>"
338+
"</ogc:Filter>" );
339+
340+
QTest::newRow( "ilike" ) << QString( "NAME ILIKE '*QGIS*'" ) << QString(
341+
"<ogc:Filter xmlns:ogc=\"http://www.opengis.net/ogc\">"
342+
"<ogc:PropertyIsLike matchCase=\"false\" singleChar=\"_\" escape=\"\\\" wildCard=\"%\">"
343+
"<ogc:PropertyName>NAME</ogc:PropertyName>"
344+
"<ogc:Literal>*QGIS*</ogc:Literal>"
345+
"</ogc:PropertyIsLike>"
346+
"</ogc:Filter>" );
347+
304348
QTest::newRow( "is null" ) << QString( "A IS NULL" ) << QString(
305349
"<ogc:Filter xmlns:ogc=\"http://www.opengis.net/ogc\">"
306350
"<ogc:PropertyIsNull>"

0 commit comments

Comments
 (0)
Please sign in to comment.