Skip to content

Commit

Permalink
Use QLocale to parse string representation of numbers
Browse files Browse the repository at this point in the history
Because we now might have thousand separators, also
fixes the NULL issue reported by DelazJ.

Comes with many test cases.
  • Loading branch information
elpaso committed Jun 10, 2018
1 parent cb3eb96 commit 85b9d37
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 10 deletions.
72 changes: 62 additions & 10 deletions src/core/qgsfield.cpp
Expand Up @@ -277,6 +277,68 @@ bool QgsField::convertCompatible( QVariant &v ) const
return false;
}

// Give it a chance to convert to double since for not '.' locales
// we accept both comma and dot as decimal point
if ( d->type == QVariant::Double && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
double d = QLocale().toDouble( v.toString(), &ok );
if ( ok )
{
v = QVariant( d );
return true;
}
// For not 'dot' locales, we also want to accept '.'
if ( QLocale().decimalPoint() != '.' )
{
d = QLocale( QLocale::English ).toDouble( v.toString(), &ok );
if ( ok )
{
v = QVariant( d );
return true;
}
}
}
}

// For string representation of an int we also might have thousand separator
if ( d->type == QVariant::Int && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
int i = QLocale().toInt( v.toString(), &ok );
if ( ok )
{
v = QVariant( i );
return true;
}
}
}

// For string representation of a long we also might have thousand separator
if ( d->type == QVariant::LongLong && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
qlonglong l = QLocale().toLongLong( v.toString(), &ok );
if ( ok )
{
v = QVariant( l );
return true;
}
}
}

//String representations of doubles in QVariant will return false to convert( QVariant::Int )
//work around this by first converting to double, and then checking whether the double is convertible to int
if ( d->type == QVariant::Int && v.canConvert( QVariant::Double ) )
Expand All @@ -301,16 +363,6 @@ bool QgsField::convertCompatible( QVariant &v ) const
return true;
}

// Give it a chance to convert to double since for not '.' locales
// we accept both comma and dot as decimal point
if ( d->type == QVariant::Double && QLocale().decimalPoint() != '.' )
{
QVariant tmp( v );
if ( d->type == QVariant::Double && !tmp.convert( d->type ) )
{
v = v.toString().replace( QLocale().decimalPoint(), '.' );
}
}

if ( !v.convert( d->type ) )
{
Expand Down
87 changes: 87 additions & 0 deletions tests/src/core/testqgsfield.cpp
Expand Up @@ -498,6 +498,44 @@ void TestQgsField::convertCompatible()
QCOMPARE( longlong.type(), QVariant::LongLong );
QCOMPARE( longlong, QVariant( 99999999999999999LL ) );

//string representation of an int
QVariant stringInt( "123456" );
QVERIFY( intField.convertCompatible( stringInt ) );
QCOMPARE( stringInt.type(), QVariant::Int );
QCOMPARE( stringInt, QVariant( 123456 ) );
// now with group separator for english locale
stringInt = QVariant( "123,456" );
QVERIFY( intField.convertCompatible( stringInt ) );
QCOMPARE( stringInt.type(), QVariant::Int );
QCOMPARE( stringInt, QVariant( "123456" ) );

//string representation of a longlong
QVariant stringLong( "99999999999999999" );
QVERIFY( longlongField.convertCompatible( stringLong ) );
QCOMPARE( stringLong.type(), QVariant::LongLong );
QCOMPARE( stringLong, QVariant( 99999999999999999LL ) );
// now with group separator for english locale
stringLong = QVariant( "99,999,999,999,999,999" );
QVERIFY( longlongField.convertCompatible( stringLong ) );
QCOMPARE( stringLong.type(), QVariant::LongLong );
QCOMPARE( stringLong, QVariant( 99999999999999999LL ) );


//string representation of a double
QVariant stringDouble( "123456.012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 123456.012345 ) );
// now with group separator for english locale
stringDouble = QVariant( "1,223,456.012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 1223456.012345 ) );
// This should not convert
stringDouble = QVariant( "1.223.456,012345" );
QVERIFY( ! doubleField.convertCompatible( stringDouble ) );


//double with precision
QgsField doubleWithPrecField( QStringLiteral( "double" ), QVariant::Double, QStringLiteral( "double" ), 10, 3 );
doubleVar = QVariant( 10.12345678 );
Expand All @@ -513,12 +551,61 @@ void TestQgsField::convertCompatible()
QCOMPARE( stringVar.type(), QVariant::String );
QCOMPARE( stringVar.toString(), QString( "lon" ) );


/////////////////////////////////////////////////////////
// German locale tests

//double with ',' as decimal separator for German locale
QLocale::setDefault( QLocale::German );
QVariant doubleCommaVar( "1,2345" );
QVERIFY( doubleField.convertCompatible( doubleCommaVar ) );
QCOMPARE( doubleCommaVar.type(), QVariant::Double );
QCOMPARE( doubleCommaVar.toString(), QString( "1.2345" ) );

//string representation of an int
stringInt = QVariant( "123456" );
QVERIFY( intField.convertCompatible( stringInt ) );
QCOMPARE( stringInt.type(), QVariant::Int );
QCOMPARE( stringInt, QVariant( 123456 ) );
// now with group separator for german locale
stringInt = QVariant( "123.456" );
QVERIFY( intField.convertCompatible( stringInt ) );
QCOMPARE( stringInt.type(), QVariant::Int );
QCOMPARE( stringInt, QVariant( "123456" ) );

//string representation of a longlong
stringLong = QVariant( "99999999999999999" );
QVERIFY( longlongField.convertCompatible( stringLong ) );
QCOMPARE( stringLong.type(), QVariant::LongLong );
QCOMPARE( stringLong, QVariant( 99999999999999999LL ) );
// now with group separator for german locale
stringLong = QVariant( "99.999.999.999.999.999" );
QVERIFY( longlongField.convertCompatible( stringLong ) );
QCOMPARE( stringLong.type(), QVariant::LongLong );
QCOMPARE( stringLong, QVariant( 99999999999999999LL ) );

//string representation of a double
stringDouble = QVariant( "123456,012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 123456.012345 ) );
// For doubles we also want to accept dot as a decimal point
stringDouble = QVariant( "123456.012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 123456.012345 ) );
// now with group separator for german locale
stringDouble = QVariant( "1.223.456,012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 1223456.012345 ) );
// Be are good citizens and we also accept english locale
stringDouble = QVariant( "1,223,456.012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 1223456.012345 ) );


}

void TestQgsField::dataStream()
Expand Down

0 comments on commit 85b9d37

Please sign in to comment.