1 /***************************************************************************
2 qgsfield.cpp - Describes a field in a layer or table
3 --------------------------------------
4 Date : 01-Jan-2004
5 Copyright : (C) 2004 by Gary E.Sherman
6 email : sherman at mrcc.com
7
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "qgsfields.h"
18 #include "qgsfield_p.h"
19 #include "qgis.h"
20 #include "qgsapplication.h"
21 #include "qgssettings.h"
22
23 #include <QDataStream>
24 #include <QIcon>
25 #include <QLocale>
26 #include <QJsonDocument>
27
28 /***************************************************************************
29 * This class is considered CRITICAL and any change MUST be accompanied with
30 * full unit tests in testqgsfield.cpp.
31 * See details in QEP #17
32 ****************************************************************************/
33
34 #if 0
35 QgsField::QgsField( QString nam, QString typ, int len, int prec, bool num,
36 QString comment )
37 : mName( nam ), mType( typ ), mLength( len ), mPrecision( prec ), mNumeric( num )
38 , mComment( comment )
39 {
40 // This function used to lower case the field name since some stores
41 // use upper case (e.g., shapefiles), but that caused problems with
42 // attribute actions getting confused between uppercase and
43 // lowercase versions of the attribute names, so just leave the
44 // names how they are now.
45 }
46 #endif
QgsField(const QString & name,QVariant::Type type,const QString & typeName,int len,int prec,const QString & comment,QVariant::Type subType)47 QgsField::QgsField( const QString &name, QVariant::Type type,
48 const QString &typeName, int len, int prec, const QString &comment, QVariant::Type subType )
49 {
50 d = new QgsFieldPrivate( name, type, subType, typeName, len, prec, comment );
51 }
52
QgsField(const QgsField & other)53 QgsField::QgsField( const QgsField &other ) //NOLINT
54 : d( other.d )
55 {
56
57 }
58
59 QgsField::~QgsField() = default;
60
61 /***************************************************************************
62 * This class is considered CRITICAL and any change MUST be accompanied with
63 * full unit tests in testqgsfield.cpp.
64 * See details in QEP #17
65 ****************************************************************************/
66
operator =(const QgsField & other)67 QgsField &QgsField::operator =( const QgsField &other ) //NOLINT
68 {
69 d = other.d;
70 return *this;
71 }
72
operator ==(const QgsField & other) const73 bool QgsField::operator==( const QgsField &other ) const
74 {
75 return *( other.d ) == *d;
76 }
77
operator !=(const QgsField & other) const78 bool QgsField::operator!=( const QgsField &other ) const
79 {
80 return !( *this == other );
81 }
82
name() const83 QString QgsField::name() const
84 {
85 return d->name;
86 }
87
displayName() const88 QString QgsField::displayName() const
89 {
90 if ( !d->alias.isEmpty() )
91 return d->alias;
92 else
93 return d->name;
94 }
95
displayNameWithAlias() const96 QString QgsField::displayNameWithAlias() const
97 {
98 if ( alias().isEmpty() )
99 {
100 return name();
101 }
102 return QStringLiteral( "%1 (%2)" ).arg( name() ).arg( alias() );
103 }
104
displayType(const bool showConstraints) const105 QString QgsField::displayType( const bool showConstraints ) const
106 {
107 QString typeStr = typeName();
108
109 if ( length() > 0 && precision() > 0 )
110 typeStr += QStringLiteral( "(%1, %2)" ).arg( length() ).arg( precision() );
111 else if ( length() > 0 )
112 typeStr += QStringLiteral( "(%1)" ).arg( length() );
113
114 if ( showConstraints )
115 {
116 typeStr += ( constraints().constraints() & QgsFieldConstraints::ConstraintNotNull )
117 ? QStringLiteral( " NOT NULL" )
118 : QStringLiteral( " NULL" );
119
120 typeStr += ( constraints().constraints() & QgsFieldConstraints::ConstraintUnique )
121 ? QStringLiteral( " UNIQUE" )
122 : QString();
123 }
124
125 return typeStr;
126 }
127
type() const128 QVariant::Type QgsField::type() const
129 {
130 return d->type;
131 }
132
subType() const133 QVariant::Type QgsField::subType() const
134 {
135 return d->subType;
136 }
137
typeName() const138 QString QgsField::typeName() const
139 {
140 return d->typeName;
141 }
142
length() const143 int QgsField::length() const
144 {
145 return d->length;
146 }
147
precision() const148 int QgsField::precision() const
149 {
150 return d->precision;
151 }
152
comment() const153 QString QgsField::comment() const
154 {
155 return d->comment;
156 }
157
isNumeric() const158 bool QgsField::isNumeric() const
159 {
160 return d->type == QVariant::Double || d->type == QVariant::Int || d->type == QVariant::UInt || d->type == QVariant::LongLong || d->type == QVariant::ULongLong;
161 }
162
isDateOrTime() const163 bool QgsField::isDateOrTime() const
164 {
165 return d->type == QVariant::Date || d->type == QVariant::Time || d->type == QVariant::DateTime;
166 }
167
168 /***************************************************************************
169 * This class is considered CRITICAL and any change MUST be accompanied with
170 * full unit tests in testqgsfield.cpp.
171 * See details in QEP #17
172 ****************************************************************************/
173
setName(const QString & name)174 void QgsField::setName( const QString &name )
175 {
176 d->name = name;
177 }
178
setType(QVariant::Type type)179 void QgsField::setType( QVariant::Type type )
180 {
181 d->type = type;
182 }
183
setSubType(QVariant::Type subType)184 void QgsField::setSubType( QVariant::Type subType )
185 {
186 d->subType = subType;
187 }
188
setTypeName(const QString & typeName)189 void QgsField::setTypeName( const QString &typeName )
190 {
191 d->typeName = typeName;
192 }
193
setLength(int len)194 void QgsField::setLength( int len )
195 {
196 d->length = len;
197 }
setPrecision(int precision)198 void QgsField::setPrecision( int precision )
199 {
200 d->precision = precision;
201 }
202
setComment(const QString & comment)203 void QgsField::setComment( const QString &comment )
204 {
205 d->comment = comment;
206 }
207
defaultValueDefinition() const208 QgsDefaultValue QgsField::defaultValueDefinition() const
209 {
210 return d->defaultValueDefinition;
211 }
212
setDefaultValueDefinition(const QgsDefaultValue & defaultValueDefinition)213 void QgsField::setDefaultValueDefinition( const QgsDefaultValue &defaultValueDefinition )
214 {
215 d->defaultValueDefinition = defaultValueDefinition;
216 }
217
setConstraints(const QgsFieldConstraints & constraints)218 void QgsField::setConstraints( const QgsFieldConstraints &constraints )
219 {
220 d->constraints = constraints;
221 }
222
constraints() const223 const QgsFieldConstraints &QgsField::constraints() const
224 {
225 return d->constraints;
226 }
227
alias() const228 QString QgsField::alias() const
229 {
230 return d->alias;
231 }
232
setAlias(const QString & alias)233 void QgsField::setAlias( const QString &alias )
234 {
235 d->alias = alias;
236 }
237
configurationFlags() const238 QgsField::ConfigurationFlags QgsField::configurationFlags() const
239 {
240 return d->flags;
241 }
242
setConfigurationFlags(QgsField::ConfigurationFlags flags)243 void QgsField::setConfigurationFlags( QgsField::ConfigurationFlags flags )
244 {
245 d->flags = flags;
246 }
247
248 /***************************************************************************
249 * This class is considered CRITICAL and any change MUST be accompanied with
250 * full unit tests in testqgsfield.cpp.
251 * See details in QEP #17
252 ****************************************************************************/
253
displayString(const QVariant & v) const254 QString QgsField::displayString( const QVariant &v ) const
255 {
256 if ( v.isNull() )
257 {
258 return QgsApplication::nullRepresentation();
259 }
260
261 // Special treatment for numeric types if group separator is set or decimalPoint is not a dot
262 if ( d->type == QVariant::Double )
263 {
264 // if value doesn't contain a double (a default value expression for instance),
265 // apply no transformation
266 bool ok;
267 v.toDouble( &ok );
268 if ( !ok )
269 return v.toString();
270
271 // Locales with decimal point != '.' or that require group separator: use QLocale
272 if ( QLocale().decimalPoint() != '.' ||
273 !( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
274 {
275 if ( d->precision > 0 )
276 {
277 if ( -1 < v.toDouble() && v.toDouble() < 1 )
278 {
279 return QLocale().toString( v.toDouble(), 'g', d->precision );
280 }
281 else
282 {
283 return QLocale().toString( v.toDouble(), 'f', d->precision );
284 }
285 }
286 else
287 {
288 // Precision is not set, let's guess it from the
289 // standard conversion to string
290 QString s( v.toString() );
291 int dotPosition( s.indexOf( '.' ) );
292 int precision;
293 if ( dotPosition < 0 && s.indexOf( 'e' ) < 0 )
294 {
295 precision = 0;
296 return QLocale().toString( v.toDouble(), 'f', precision );
297 }
298 else
299 {
300 if ( dotPosition < 0 ) precision = 0;
301 else precision = s.length() - dotPosition - 1;
302
303 if ( -1 < v.toDouble() && v.toDouble() < 1 )
304 {
305 return QLocale().toString( v.toDouble(), 'g', precision );
306 }
307 else
308 {
309 return QLocale().toString( v.toDouble(), 'f', precision );
310 }
311 }
312 }
313 }
314 // Default for doubles with precision
315 else if ( d->precision > 0 )
316 {
317 if ( -1 < v.toDouble() && v.toDouble() < 1 )
318 {
319 return QString::number( v.toDouble(), 'g', d->precision );
320 }
321 else
322 {
323 return QString::number( v.toDouble(), 'f', d->precision );
324 }
325 }
326 }
327 // Other numeric types than doubles
328 else if ( isNumeric() &&
329 !( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
330 {
331 bool ok;
332 qlonglong converted( v.toLongLong( &ok ) );
333 if ( ok )
334 return QLocale().toString( converted );
335 }
336 else if ( d->typeName.compare( QLatin1String( "json" ), Qt::CaseInsensitive ) == 0 || d->typeName == QLatin1String( "jsonb" ) )
337 {
338 QJsonDocument doc = QJsonDocument::fromVariant( v );
339 return QString::fromUtf8( doc.toJson().data() );
340 }
341 else if ( d->type == QVariant::ByteArray )
342 {
343 return QObject::tr( "BLOB" );
344 }
345 else if ( d->type == QVariant::StringList || d->type == QVariant::List )
346 {
347 QString result;
348 const QVariantList list = v.toList();
349 for ( const QVariant &var : list )
350 {
351 if ( !result.isEmpty() )
352 result.append( QStringLiteral( ", " ) );
353 result.append( var.toString() );
354 }
355 return result;
356 }
357
358 // Fallback if special rules do not apply
359 return v.toString();
360 }
361
readableConfigurationFlag(QgsField::ConfigurationFlag flag)362 QString QgsField::readableConfigurationFlag( QgsField::ConfigurationFlag flag )
363 {
364 switch ( flag )
365 {
366 case ConfigurationFlag::None:
367 return QObject::tr( "None" );
368 case ConfigurationFlag::NotSearchable:
369 return QObject::tr( "Not searchable" );
370 case ConfigurationFlag::HideFromWms:
371 return QObject::tr( "Do not expose via WMS" );
372 case ConfigurationFlag::HideFromWfs:
373 return QObject::tr( "Do not expose via WFS" );
374 }
375 return QString();
376 }
377
378 /***************************************************************************
379 * This class is considered CRITICAL and any change MUST be accompanied with
380 * full unit tests in testqgsfield.cpp.
381 * See details in QEP #17
382 ****************************************************************************/
383
convertCompatible(QVariant & v,QString * errorMessage) const384 bool QgsField::convertCompatible( QVariant &v, QString *errorMessage ) const
385 {
386 const QVariant original = v;
387 if ( errorMessage )
388 errorMessage->clear();
389
390 if ( v.isNull() )
391 {
392 v.convert( d->type );
393 return true;
394 }
395
396 if ( d->type == QVariant::Int && v.toInt() != v.toLongLong() )
397 {
398 v = QVariant( d->type );
399 if ( errorMessage )
400 *errorMessage = QObject::tr( "Value \"%1\" is too large for integer field" ).arg( original.toLongLong() );
401 return false;
402 }
403
404 // Give it a chance to convert to double since for not '.' locales
405 // we accept both comma and dot as decimal point
406 if ( d->type == QVariant::Double && v.type() == QVariant::String )
407 {
408 QVariant tmp( v );
409 if ( !tmp.convert( d->type ) )
410 {
411 // This might be a string with thousand separator: use locale to convert
412 bool ok = false;
413 double d = qgsPermissiveToDouble( v.toString(), ok );
414 if ( ok )
415 {
416 v = QVariant( d );
417 return true;
418 }
419 // For not 'dot' locales, we also want to accept '.'
420 if ( QLocale().decimalPoint() != '.' )
421 {
422 d = QLocale( QLocale::C ).toDouble( v.toString(), &ok );
423 if ( ok )
424 {
425 v = QVariant( d );
426 return true;
427 }
428 }
429 }
430 }
431
432 // For string representation of an int we also might have thousand separator
433 if ( d->type == QVariant::Int && v.type() == QVariant::String )
434 {
435 QVariant tmp( v );
436 if ( !tmp.convert( d->type ) )
437 {
438 // This might be a string with thousand separator: use locale to convert
439 bool ok;
440 int i = qgsPermissiveToInt( v.toString(), ok );
441 if ( ok )
442 {
443 v = QVariant( i );
444 return true;
445 }
446 }
447 }
448
449 // For string representation of a long we also might have thousand separator
450 if ( d->type == QVariant::LongLong && v.type() == QVariant::String )
451 {
452 QVariant tmp( v );
453 if ( !tmp.convert( d->type ) )
454 {
455 // This might be a string with thousand separator: use locale to convert
456 bool ok;
457 qlonglong l = qgsPermissiveToLongLong( v.toString(), ok );
458 if ( ok )
459 {
460 v = QVariant( l );
461 return true;
462 }
463 }
464 }
465
466 //String representations of doubles in QVariant will return false to convert( QVariant::Int )
467 //work around this by first converting to double, and then checking whether the double is convertible to int
468 if ( d->type == QVariant::Int && v.canConvert( QVariant::Double ) )
469 {
470 bool ok = false;
471 double dbl = v.toDouble( &ok );
472 if ( !ok )
473 {
474 //couldn't convert to number
475 v = QVariant( d->type );
476
477 if ( errorMessage )
478 *errorMessage = QObject::tr( "Value \"%1\" is not a number" ).arg( original.toString() );
479
480 return false;
481 }
482
483 double round = std::round( dbl );
484 if ( round > std::numeric_limits<int>::max() || round < -std::numeric_limits<int>::max() )
485 {
486 //double too large to fit in int
487 v = QVariant( d->type );
488
489 if ( errorMessage )
490 *errorMessage = QObject::tr( "Value \"%1\" is too large for integer field" ).arg( original.toDouble() );
491
492 return false;
493 }
494 v = QVariant( static_cast< int >( std::round( dbl ) ) );
495 return true;
496 }
497
498 //String representations of doubles in QVariant will return false to convert( QVariant::LongLong )
499 //work around this by first converting to double, and then checking whether the double is convertible to longlong
500 if ( d->type == QVariant::LongLong && v.canConvert( QVariant::Double ) )
501 {
502 //firstly test the conversion to longlong because conversion to double will rounded the value
503 QVariant tmp( v );
504 if ( !tmp.convert( d->type ) )
505 {
506 bool ok = false;
507 double dbl = v.toDouble( &ok );
508 if ( !ok )
509 {
510 //couldn't convert to number
511 v = QVariant( d->type );
512
513 if ( errorMessage )
514 *errorMessage = QObject::tr( "Value \"%1\" is not a number" ).arg( original.toString() );
515
516 return false;
517 }
518
519 double round = std::round( dbl );
520 if ( round > static_cast<double>( std::numeric_limits<long long>::max() ) || round < static_cast<double>( -std::numeric_limits<long long>::max() ) )
521 {
522 //double too large to fit in longlong
523 v = QVariant( d->type );
524
525 if ( errorMessage )
526 *errorMessage = QObject::tr( "Value \"%1\" is too large for long long field" ).arg( original.toDouble() );
527
528 return false;
529 }
530 v = QVariant( static_cast< long long >( std::round( dbl ) ) );
531 return true;
532 }
533 }
534
535 if ( !v.convert( d->type ) )
536 {
537 v = QVariant( d->type );
538
539 if ( errorMessage )
540 *errorMessage = QObject::tr( "Could not convert value \"%1\" to target type" ).arg( original.toString() );
541
542 return false;
543 }
544
545 if ( d->type == QVariant::Double && d->precision > 0 )
546 {
547 double s = std::pow( 10, d->precision );
548 double d = v.toDouble() * s;
549 v = QVariant( ( d < 0 ? std::ceil( d - 0.5 ) : std::floor( d + 0.5 ) ) / s );
550 return true;
551 }
552
553 if ( d->type == QVariant::String && d->length > 0 && v.toString().length() > d->length )
554 {
555 const int length = v.toString().length();
556 v = v.toString().left( d->length );
557
558 if ( errorMessage )
559 *errorMessage = QObject::tr( "String of length %1 exceeds maximum field length (%2)" ).arg( length ).arg( d->length );
560
561 return false;
562 }
563
564 return true;
565 }
566
setEditorWidgetSetup(const QgsEditorWidgetSetup & v)567 void QgsField::setEditorWidgetSetup( const QgsEditorWidgetSetup &v )
568 {
569 d->editorWidgetSetup = v;
570 }
571
editorWidgetSetup() const572 QgsEditorWidgetSetup QgsField::editorWidgetSetup() const
573 {
574 return d->editorWidgetSetup;
575 }
576
577 /***************************************************************************
578 * This class is considered CRITICAL and any change MUST be accompanied with
579 * full unit tests in testqgsfield.cpp.
580 * See details in QEP #17
581 ****************************************************************************/
582
operator <<(QDataStream & out,const QgsField & field)583 QDataStream &operator<<( QDataStream &out, const QgsField &field )
584 {
585 out << field.name();
586 out << static_cast< quint32 >( field.type() );
587 out << field.typeName();
588 out << field.length();
589 out << field.precision();
590 out << field.comment();
591 out << field.alias();
592 out << field.defaultValueDefinition().expression();
593 out << field.defaultValueDefinition().applyOnUpdate();
594 out << field.constraints().constraints();
595 out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) );
596 out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintUnique ) );
597 out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintExpression ) );
598 out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
599 out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
600 out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
601 out << field.constraints().constraintExpression();
602 out << field.constraints().constraintDescription();
603 out << static_cast< quint32 >( field.subType() );
604 return out;
605 }
606
operator >>(QDataStream & in,QgsField & field)607 QDataStream &operator>>( QDataStream &in, QgsField &field )
608 {
609 quint32 type;
610 quint32 subType;
611 quint32 length;
612 quint32 precision;
613 quint32 constraints;
614 quint32 originNotNull;
615 quint32 originUnique;
616 quint32 originExpression;
617 quint32 strengthNotNull;
618 quint32 strengthUnique;
619 quint32 strengthExpression;
620
621 bool applyOnUpdate;
622
623 QString name;
624 QString typeName;
625 QString comment;
626 QString alias;
627 QString defaultValueExpression;
628 QString constraintExpression;
629 QString constraintDescription;
630
631 in >> name >> type >> typeName >> length >> precision >> comment >> alias
632 >> defaultValueExpression >> applyOnUpdate >> constraints >> originNotNull >> originUnique >> originExpression >> strengthNotNull >> strengthUnique >> strengthExpression >>
633 constraintExpression >> constraintDescription >> subType;
634 field.setName( name );
635 field.setType( static_cast< QVariant::Type >( type ) );
636 field.setTypeName( typeName );
637 field.setLength( static_cast< int >( length ) );
638 field.setPrecision( static_cast< int >( precision ) );
639 field.setComment( comment );
640 field.setAlias( alias );
641 field.setDefaultValueDefinition( QgsDefaultValue( defaultValueExpression, applyOnUpdate ) );
642 QgsFieldConstraints fieldConstraints;
643 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
644 {
645 fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, static_cast< QgsFieldConstraints::ConstraintOrigin>( originNotNull ) );
646 fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintNotNull, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthNotNull ) );
647 }
648 else
649 fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintNotNull );
650 if ( constraints & QgsFieldConstraints::ConstraintUnique )
651 {
652 fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintUnique, static_cast< QgsFieldConstraints::ConstraintOrigin>( originUnique ) );
653 fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintUnique, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthUnique ) );
654 }
655 else
656 fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintUnique );
657 if ( constraints & QgsFieldConstraints::ConstraintExpression )
658 {
659 fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintExpression, static_cast< QgsFieldConstraints::ConstraintOrigin>( originExpression ) );
660 fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintExpression, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthExpression ) );
661 }
662 else
663 fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintExpression );
664 fieldConstraints.setConstraintExpression( constraintExpression, constraintDescription );
665 field.setConstraints( fieldConstraints );
666 field.setSubType( static_cast< QVariant::Type >( subType ) );
667 return in;
668 }
669