Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsexpressionsorter.h - QgsExpressionSorter 3 : : ------------------------------------------- 4 : : 5 : : begin : 15.1.2016 6 : : Copyright : (C) 2016 Matthias Kuhn 7 : : Email : matthias at opengis dot ch 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 : : #ifndef QGSEXPRESSIONSORTER_H 17 : : #define QGSEXPRESSIONSORTER_H 18 : : 19 : : #include <QLocale> 20 : : 21 : : #include "qgsfeaturerequest.h" 22 : : #include "qgsindexedfeature.h" 23 : : 24 : : /// @cond PRIVATE 25 : 0 : class QgsExpressionSorter 26 : : { 27 : : public: 28 : 0 : explicit QgsExpressionSorter( const QList<QgsFeatureRequest::OrderByClause> &preparedOrderBys ) 29 : 0 : : mPreparedOrderBys( preparedOrderBys ) 30 : : // QString::localeAwareCompare() is case insensitive for common locales, 31 : : // but case sensitive for the C locale. So use an explicit case 32 : : // insensitive comparison in that later case to avoid test failures. 33 : 0 : , mUseCaseInsensitiveComparison( QLocale().name() == QLocale::c().name() ) 34 : 0 : {} 35 : : 36 : 0 : bool operator()( const QgsIndexedFeature &f1, const QgsIndexedFeature &f2 ) const 37 : : { 38 : 0 : int i = 0; 39 : 0 : for ( const QgsFeatureRequest::OrderByClause &orderBy : std::as_const( mPreparedOrderBys ) ) 40 : : { 41 : 0 : const QVariant &v1 = f1.mIndexes.at( i ); 42 : 0 : const QVariant &v2 = f2.mIndexes.at( i ); 43 : 0 : ++i; 44 : : 45 : : // Both NULL: don't care 46 : 0 : if ( v1.isNull() && v2.isNull() ) 47 : 0 : continue; 48 : : 49 : : // Check for NULLs first 50 : 0 : if ( v1.isNull() != v2.isNull() ) 51 : : { 52 : 0 : if ( orderBy.nullsFirst() ) 53 : 0 : return v1.isNull(); 54 : : else 55 : 0 : return !v1.isNull(); 56 : : } 57 : : 58 : : // Both values are not NULL 59 : 0 : switch ( v1.type() ) 60 : : { 61 : : case QVariant::Int: 62 : : case QVariant::UInt: 63 : : case QVariant::LongLong: 64 : : case QVariant::ULongLong: 65 : 0 : if ( v1.toLongLong() == v2.toLongLong() ) 66 : 0 : continue; 67 : 0 : if ( orderBy.ascending() ) 68 : 0 : return v1.toLongLong() < v2.toLongLong(); 69 : : else 70 : 0 : return v1.toLongLong() > v2.toLongLong(); 71 : : 72 : : case QVariant::Double: 73 : 0 : if ( qgsDoubleNear( v1.toDouble(), v2.toDouble() ) ) 74 : 0 : continue; 75 : 0 : if ( orderBy.ascending() ) 76 : 0 : return v1.toDouble() < v2.toDouble(); 77 : : else 78 : 0 : return v1.toDouble() > v2.toDouble(); 79 : : 80 : : case QVariant::Date: 81 : 0 : if ( v1.toDate() == v2.toDate() ) 82 : 0 : continue; 83 : 0 : if ( orderBy.ascending() ) 84 : 0 : return v1.toDate() < v2.toDate(); 85 : : else 86 : 0 : return v1.toDate() > v2.toDate(); 87 : : 88 : : case QVariant::Time: 89 : 0 : if ( v1.toTime() == v2.toTime() ) 90 : 0 : continue; 91 : 0 : if ( orderBy.ascending() ) 92 : 0 : return v1.toTime() < v2.toTime(); 93 : : else 94 : 0 : return v1.toTime() > v2.toTime(); 95 : : 96 : : case QVariant::DateTime: 97 : 0 : if ( v1.toDateTime() == v2.toDateTime() ) 98 : 0 : continue; 99 : 0 : if ( orderBy.ascending() ) 100 : 0 : return v1.toDateTime() < v2.toDateTime(); 101 : : else 102 : 0 : return v1.toDateTime() > v2.toDateTime(); 103 : : 104 : : case QVariant::Bool: 105 : 0 : if ( v1.toBool() == v2.toBool() ) 106 : 0 : continue; 107 : 0 : if ( orderBy.ascending() ) 108 : 0 : return !v1.toBool(); 109 : : else 110 : 0 : return v1.toBool(); 111 : : 112 : : default: 113 : 0 : if ( 0 == v1.toString().localeAwareCompare( v2.toString() ) ) 114 : 0 : continue; 115 : 0 : if ( mUseCaseInsensitiveComparison ) 116 : : { 117 : 0 : if ( orderBy.ascending() ) 118 : 0 : return v1.toString().compare( v2.toString(), Qt::CaseInsensitive ) < 0; 119 : : else 120 : 0 : return v1.toString().compare( v2.toString(), Qt::CaseInsensitive ) > 0; 121 : : } 122 : : else 123 : : { 124 : 0 : if ( orderBy.ascending() ) 125 : 0 : return v1.toString().localeAwareCompare( v2.toString() ) < 0; 126 : : else 127 : 0 : return v1.toString().localeAwareCompare( v2.toString() ) > 0; 128 : : } 129 : : } 130 : : } 131 : : 132 : : // Equal 133 : 0 : return false; 134 : 0 : } 135 : : 136 : 0 : void sortFeatures( QList<QgsFeature> &features, QgsExpressionContext *expressionContext ) 137 : : { 138 : 0 : QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Expression Sorter" ) ); 139 : : 140 : 0 : expressionContext->appendScope( scope ); 141 : : 142 : 0 : QVector<QgsIndexedFeature> indexedFeatures; 143 : : 144 : 0 : QgsIndexedFeature indexedFeatureToAppend; 145 : : 146 : 0 : for ( const QgsFeature &f : std::as_const( features ) ) 147 : : { 148 : 0 : indexedFeatureToAppend.mIndexes.resize( mPreparedOrderBys.size() ); 149 : 0 : indexedFeatureToAppend.mFeature = f; 150 : : 151 : 0 : expressionContext->setFeature( indexedFeatureToAppend.mFeature ); 152 : : 153 : 0 : int i = 0; 154 : 0 : for ( const QgsFeatureRequest::OrderByClause &orderBy : std::as_const( mPreparedOrderBys ) ) 155 : : { 156 : 0 : indexedFeatureToAppend.mIndexes.replace( i++, orderBy.expression().evaluate( expressionContext ) ); 157 : : } 158 : 0 : indexedFeatures.append( indexedFeatureToAppend ); 159 : : } 160 : : 161 : 0 : delete expressionContext->popScope(); 162 : : 163 : 0 : std::sort( indexedFeatures.begin(), indexedFeatures.end(), *this ); 164 : : 165 : 0 : features.clear(); 166 : : 167 : 0 : for ( const QgsIndexedFeature &indexedFeature : std::as_const( indexedFeatures ) ) 168 : 0 : features.append( indexedFeature.mFeature ); 169 : 0 : } 170 : : 171 : : private: 172 : : QList<QgsFeatureRequest::OrderByClause> mPreparedOrderBys; 173 : : bool mUseCaseInsensitiveComparison; 174 : : }; 175 : : 176 : : /// @endcond 177 : : 178 : : 179 : : #endif // QGSEXPRESSIONSORTER_H