LCOV - code coverage report
Current view: top level - core - qgsvirtuallayerdefinition.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 213 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                 qgsvirtuallayerdefinition.cpp
       3                 :            : begin                : December 2015
       4                 :            : copyright            : (C) 2015 Hugo Mercier, Oslandia
       5                 :            : email                : hugo dot mercier at oslandia dot com
       6                 :            :  ***************************************************************************/
       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 <QUrl>
      18                 :            : #include <QRegExp>
      19                 :            : #include <QStringList>
      20                 :            : #include <QUrlQuery>
      21                 :            : #include <QtEndian>
      22                 :            : 
      23                 :            : #include "qgsvirtuallayerdefinition.h"
      24                 :            : #include "qgsvectorlayer.h"
      25                 :            : #include "qgsvectordataprovider.h"
      26                 :            : 
      27                 :          0 : QgsVirtualLayerDefinition::QgsVirtualLayerDefinition( const QString &filePath )
      28                 :          0 :   : mFilePath( filePath )
      29                 :            : {
      30                 :          0 : }
      31                 :            : 
      32                 :          0 : QgsVirtualLayerDefinition QgsVirtualLayerDefinition::fromUrl( const QUrl &url )
      33                 :            : {
      34                 :          0 :   QgsVirtualLayerDefinition def;
      35                 :            : 
      36                 :          0 :   def.setFilePath( url.toLocalFile() );
      37                 :            : 
      38                 :            :   // regexp for column name
      39                 :          0 :   const QString columnNameRx( QStringLiteral( "[a-zA-Z_\x80-\xFF][a-zA-Z0-9_\x80-\xFF]*" ) );
      40                 :            : 
      41                 :          0 :   QgsFields fields;
      42                 :            : 
      43                 :          0 :   int layerIdx = 0;
      44                 :            : 
      45                 :          0 :   const QList<QPair<QString, QString> > items = QUrlQuery( url ).queryItems( QUrl::FullyEncoded );
      46                 :          0 :   for ( int i = 0; i < items.size(); i++ )
      47                 :            :   {
      48                 :          0 :     QString key = items.at( i ).first;
      49                 :          0 :     QString value = items.at( i ).second;
      50                 :          0 :     if ( key == QLatin1String( "layer_ref" ) )
      51                 :            :     {
      52                 :          0 :       layerIdx++;
      53                 :            :       // layer id, with optional layer_name
      54                 :          0 :       int pos = value.indexOf( ':' );
      55                 :          0 :       QString layerId, vlayerName;
      56                 :          0 :       if ( pos == -1 )
      57                 :            :       {
      58                 :          0 :         layerId = value;
      59                 :          0 :         vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
      60                 :          0 :       }
      61                 :            :       else
      62                 :            :       {
      63                 :          0 :         layerId = value.left( pos );
      64                 :          0 :         vlayerName = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
      65                 :            :       }
      66                 :            :       // add the layer to the list
      67                 :          0 :       def.addSource( vlayerName, layerId );
      68                 :          0 :     }
      69                 :          0 :     else if ( key == QLatin1String( "layer" ) )
      70                 :            :     {
      71                 :          0 :       layerIdx++;
      72                 :            :       // syntax: layer=provider:url_encoded_source_URI(:name(:encoding)?)?
      73                 :          0 :       int pos = value.indexOf( ':' );
      74                 :          0 :       if ( pos != -1 )
      75                 :            :       {
      76                 :          0 :         QString providerKey, source, vlayerName, encoding = QStringLiteral( "UTF-8" );
      77                 :            : 
      78                 :          0 :         providerKey = value.left( pos );
      79                 :          0 :         int pos2 = value.indexOf( ':', pos + 1 );
      80                 :          0 :         if ( pos2 - pos == 2 )
      81                 :          0 :           pos2 = value.indexOf( ':', pos + 3 );
      82                 :          0 :         if ( pos2 != -1 )
      83                 :            :         {
      84                 :          0 :           source = QUrl::fromPercentEncoding( value.mid( pos + 1, pos2 - pos - 1 ).toUtf8() );
      85                 :          0 :           int pos3 = value.indexOf( ':', pos2 + 1 );
      86                 :          0 :           if ( pos3 != -1 )
      87                 :            :           {
      88                 :          0 :             vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1, pos3 - pos2 - 1 ).toUtf8() );
      89                 :          0 :             encoding = value.mid( pos3 + 1 );
      90                 :          0 :           }
      91                 :            :           else
      92                 :            :           {
      93                 :          0 :             vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1 ).toUtf8() );
      94                 :            :           }
      95                 :          0 :         }
      96                 :            :         else
      97                 :            :         {
      98                 :          0 :           source = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
      99                 :          0 :           vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
     100                 :            :         }
     101                 :            : 
     102                 :          0 :         def.addSource( vlayerName, source, providerKey, encoding );
     103                 :          0 :       }
     104                 :          0 :     }
     105                 :          0 :     else if ( key == QLatin1String( "geometry" ) )
     106                 :            :     {
     107                 :            :       // geometry field definition, optional
     108                 :            :       // geometry_column(:wkb_type:srid)?
     109                 :          0 :       QRegExp reGeom( "(" + columnNameRx + ")(?::([a-zA-Z0-9]+):(\\d+))?" );
     110                 :          0 :       int pos = reGeom.indexIn( value );
     111                 :          0 :       if ( pos >= 0 )
     112                 :            :       {
     113                 :          0 :         def.setGeometryField( reGeom.cap( 1 ) );
     114                 :          0 :         if ( reGeom.captureCount() > 1 )
     115                 :            :         {
     116                 :            :           // not used by the spatialite provider for now ...
     117                 :          0 :           QgsWkbTypes::Type wkbType = QgsWkbTypes::parseType( reGeom.cap( 2 ) );
     118                 :          0 :           if ( wkbType == QgsWkbTypes::Unknown )
     119                 :            :           {
     120                 :          0 :             wkbType = static_cast<QgsWkbTypes::Type>( reGeom.cap( 2 ).toLong() );
     121                 :          0 :           }
     122                 :          0 :           def.setGeometryWkbType( wkbType );
     123                 :          0 :           def.setGeometrySrid( reGeom.cap( 3 ).toLong() );
     124                 :          0 :         }
     125                 :          0 :       }
     126                 :          0 :     }
     127                 :          0 :     else if ( key == QLatin1String( "nogeometry" ) )
     128                 :            :     {
     129                 :          0 :       def.setGeometryWkbType( QgsWkbTypes::NoGeometry );
     130                 :          0 :     }
     131                 :          0 :     else if ( key == QLatin1String( "uid" ) )
     132                 :            :     {
     133                 :          0 :       def.setUid( value );
     134                 :          0 :     }
     135                 :          0 :     else if ( key == QLatin1String( "query" ) )
     136                 :            :     {
     137                 :            :       // url encoded query
     138                 :          0 :       def.setQuery( QUrl::fromPercentEncoding( value.toUtf8() ) );
     139                 :          0 :     }
     140                 :          0 :     else if ( key == QLatin1String( "field" ) )
     141                 :            :     {
     142                 :            :       // field_name:type (int, real, text)
     143                 :          0 :       QRegExp reField( "(" + columnNameRx + "):(int|real|text)" );
     144                 :          0 :       int pos = reField.indexIn( value );
     145                 :          0 :       if ( pos >= 0 )
     146                 :            :       {
     147                 :          0 :         QString fieldName( reField.cap( 1 ) );
     148                 :          0 :         QString fieldType( reField.cap( 2 ) );
     149                 :          0 :         if ( fieldType == QLatin1String( "int" ) )
     150                 :            :         {
     151                 :          0 :           fields.append( QgsField( fieldName, QVariant::LongLong, fieldType ) );
     152                 :          0 :         }
     153                 :          0 :         else if ( fieldType == QLatin1String( "real" ) )
     154                 :            :         {
     155                 :          0 :           fields.append( QgsField( fieldName, QVariant::Double, fieldType ) );
     156                 :          0 :         }
     157                 :          0 :         if ( fieldType == QLatin1String( "text" ) )
     158                 :            :         {
     159                 :          0 :           fields.append( QgsField( fieldName, QVariant::String, fieldType ) );
     160                 :          0 :         }
     161                 :          0 :       }
     162                 :          0 :     }
     163                 :          0 :     else if ( key == QLatin1String( "lazy" ) )
     164                 :            :     {
     165                 :          0 :       def.setLazy( true );
     166                 :          0 :     }
     167                 :          0 :     else if ( key == QLatin1String( "subsetstring" ) )
     168                 :            :     {
     169                 :          0 :       def.setSubsetString( QUrl::fromPercentEncoding( value.toUtf8() ) );
     170                 :          0 :     }
     171                 :          0 :   }
     172                 :          0 :   def.setFields( fields );
     173                 :            : 
     174                 :          0 :   return def;
     175                 :          0 : }
     176                 :            : 
     177                 :            : // Mega ewwww. all this is taken from Qt's QUrl::addEncodedQueryItem compatibility helper.
     178                 :            : // (I can't see any way to port the below code to NOT require this without breaking
     179                 :            : // existing projects.)
     180                 :            : 
     181                 :          0 : inline char toHexUpper( uint value ) noexcept
     182                 :            : {
     183                 :          0 :   return "0123456789ABCDEF"[value & 0xF];
     184                 :            : }
     185                 :            : 
     186                 :          0 : static inline ushort encodeNibble( ushort c )
     187                 :            : {
     188                 :          0 :   return ushort( toHexUpper( c ) );
     189                 :            : }
     190                 :            : 
     191                 :          0 : static bool qt_is_ascii( const char *&ptr, const char *end ) noexcept
     192                 :            : {
     193                 :          0 :   while ( ptr + 4 <= end )
     194                 :            :   {
     195                 :          0 :     quint32 data = qFromUnaligned<quint32>( ptr );
     196                 :          0 :     if ( data &= 0x80808080U )
     197                 :            :     {
     198                 :            : #if Q_BYTE_ORDER == Q_BIG_ENDIAN
     199                 :            :       uint idx = qCountLeadingZeroBits( data );
     200                 :            : #else
     201                 :          0 :       uint idx = qCountTrailingZeroBits( data );
     202                 :            : #endif
     203                 :          0 :       ptr += idx / 8;
     204                 :          0 :       return false;
     205                 :            :     }
     206                 :          0 :     ptr += 4;
     207                 :            :   }
     208                 :          0 :   while ( ptr != end )
     209                 :          0 :   {
     210                 :          0 :     if ( quint8( *ptr ) & 0x80 )
     211                 :          0 :       return false;
     212                 :          0 :     ++ptr;
     213                 :            :   }
     214                 :          0 :   return true;
     215                 :          0 : }
     216                 :            : 
     217                 :          0 : QString fromEncodedComponent_helper( const QByteArray &ba )
     218                 :            : {
     219                 :          0 :   if ( ba.isNull() )
     220                 :          0 :     return QString();
     221                 :            :   // scan ba for anything above or equal to 0x80
     222                 :            :   // control points below 0x20 are fine in QString
     223                 :          0 :   const char *in = ba.constData();
     224                 :          0 :   const char *const end = ba.constEnd();
     225                 :          0 :   if ( qt_is_ascii( in, end ) )
     226                 :            :   {
     227                 :            :     // no non-ASCII found, we're safe to convert to QString
     228                 :          0 :     return QString::fromLatin1( ba, ba.size() );
     229                 :            :   }
     230                 :            :   // we found something that we need to encode
     231                 :          0 :   QByteArray intermediate = ba;
     232                 :          0 :   intermediate.resize( ba.size() * 3 - ( in - ba.constData() ) );
     233                 :          0 :   uchar *out = reinterpret_cast<uchar *>( intermediate.data() + ( in - ba.constData() ) );
     234                 :          0 :   for ( ; in < end; ++in )
     235                 :            :   {
     236                 :          0 :     if ( *in & 0x80 )
     237                 :            :     {
     238                 :            :       // encode
     239                 :          0 :       *out++ = '%';
     240                 :          0 :       *out++ = encodeNibble( uchar( *in ) >> 4 );
     241                 :          0 :       *out++ = encodeNibble( uchar( *in ) & 0xf );
     242                 :          0 :     }
     243                 :            :     else
     244                 :            :     {
     245                 :            :       // keep
     246                 :          0 :       *out++ = uchar( *in );
     247                 :            :     }
     248                 :          0 :   }
     249                 :            :   // now it's safe to call fromLatin1
     250                 :          0 :   return QString::fromLatin1( intermediate, out - reinterpret_cast<uchar *>( intermediate.data() ) );
     251                 :          0 : }
     252                 :            : 
     253                 :          0 : QUrl QgsVirtualLayerDefinition::toUrl() const
     254                 :            : {
     255                 :          0 :   QUrl url;
     256                 :          0 :   if ( !filePath().isEmpty() )
     257                 :          0 :     url = QUrl::fromLocalFile( filePath() );
     258                 :            : 
     259                 :          0 :   QUrlQuery urlQuery( url );
     260                 :            : 
     261                 :          0 :   const auto constSourceLayers = sourceLayers();
     262                 :          0 :   for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
     263                 :            :   {
     264                 :          0 :     if ( l.isReferenced() )
     265                 :          0 :       urlQuery.addQueryItem( QStringLiteral( "layer_ref" ), QStringLiteral( "%1:%2" ).arg( l.reference(), l.name() ) );
     266                 :            :     else
     267                 :            :       // if you can find a way to port this away from fromEncodedComponent_helper without breaking existing projects,
     268                 :            :       // please do so... this is GROSS!
     269                 :          0 :       urlQuery.addQueryItem( fromEncodedComponent_helper( "layer" ),
     270                 :          0 :                              fromEncodedComponent_helper( QStringLiteral( "%1:%4:%2:%3" ) // the order is important, since the 4th argument may contain '%2' as well
     271                 :          0 :                                  .arg( l.provider(),
     272                 :          0 :                                        QString( QUrl::toPercentEncoding( l.name() ) ),
     273                 :          0 :                                        l.encoding(),
     274                 :          0 :                                        QString( QUrl::toPercentEncoding( l.source() ) ) ).toUtf8() ) );
     275                 :            :   }
     276                 :            : 
     277                 :          0 :   if ( !query().isEmpty() )
     278                 :            :   {
     279                 :          0 :     urlQuery.addQueryItem( QStringLiteral( "query" ), query() );
     280                 :          0 :   }
     281                 :            : 
     282                 :          0 :   if ( !uid().isEmpty() )
     283                 :          0 :     urlQuery.addQueryItem( QStringLiteral( "uid" ), uid() );
     284                 :            : 
     285                 :          0 :   if ( geometryWkbType() == QgsWkbTypes::NoGeometry )
     286                 :          0 :     urlQuery.addQueryItem( QStringLiteral( "nogeometry" ), QString() );
     287                 :          0 :   else if ( !geometryField().isEmpty() )
     288                 :            :   {
     289                 :          0 :     if ( hasDefinedGeometry() )
     290                 :          0 :       urlQuery.addQueryItem( QStringLiteral( "geometry" ), QStringLiteral( "%1:%2:%3" ).arg( geometryField() ). arg( geometryWkbType() ).arg( geometrySrid() ).toUtf8() );
     291                 :            :     else
     292                 :          0 :       urlQuery.addQueryItem( QStringLiteral( "geometry" ), geometryField() );
     293                 :          0 :   }
     294                 :            : 
     295                 :          0 :   const auto constFields = fields();
     296                 :          0 :   for ( const QgsField &f : constFields )
     297                 :            :   {
     298                 :          0 :     if ( f.type() == QVariant::Int
     299                 :          0 :          || f.type() == QVariant::UInt
     300                 :          0 :          || f.type() == QVariant::Bool
     301                 :          0 :          || f.type() == QVariant::LongLong )
     302                 :          0 :       urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":int" );
     303                 :          0 :     else if ( f.type() == QVariant::Double )
     304                 :          0 :       urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":real" );
     305                 :          0 :     else if ( f.type() == QVariant::String )
     306                 :          0 :       urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":text" );
     307                 :            :   }
     308                 :            : 
     309                 :          0 :   if ( isLazy() )
     310                 :            :   {
     311                 :          0 :     urlQuery.addQueryItem( QStringLiteral( "lazy" ), QString() );
     312                 :          0 :   }
     313                 :            : 
     314                 :          0 :   if ( ! subsetString().isEmpty() )
     315                 :            :   {
     316                 :          0 :     urlQuery.addQueryItem( QStringLiteral( "subsetstring" ), QUrl::toPercentEncoding( subsetString() ) );
     317                 :          0 :   }
     318                 :            : 
     319                 :          0 :   url.setQuery( urlQuery );
     320                 :            : 
     321                 :          0 :   return url;
     322                 :          0 : }
     323                 :            : 
     324                 :          0 : QString QgsVirtualLayerDefinition::toString() const
     325                 :            : {
     326                 :          0 :   return QString( toUrl().toEncoded() );
     327                 :          0 : }
     328                 :            : 
     329                 :          0 : void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &ref )
     330                 :            : {
     331                 :          0 :   mSourceLayers.append( SourceLayer( name, ref ) );
     332                 :          0 : }
     333                 :            : 
     334                 :          0 : void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &source, const QString &provider, const QString &encoding )
     335                 :            : {
     336                 :          0 :   mSourceLayers.append( SourceLayer( name, source, provider, encoding ) );
     337                 :          0 : }
     338                 :            : 
     339                 :          0 : bool QgsVirtualLayerDefinition::hasSourceLayer( const QString &name ) const
     340                 :            : {
     341                 :          0 :   const auto constSourceLayers = sourceLayers();
     342                 :          0 :   for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
     343                 :            :   {
     344                 :          0 :     if ( l.name() == name )
     345                 :            :     {
     346                 :          0 :       return true;
     347                 :            :     }
     348                 :            :   }
     349                 :          0 :   return false;
     350                 :          0 : }
     351                 :            : 
     352                 :          0 : bool QgsVirtualLayerDefinition::hasReferencedLayers() const
     353                 :            : {
     354                 :          0 :   const auto constSourceLayers = sourceLayers();
     355                 :          0 :   for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
     356                 :            :   {
     357                 :          0 :     if ( l.isReferenced() )
     358                 :            :     {
     359                 :          0 :       return true;
     360                 :            :     }
     361                 :            :   }
     362                 :          0 :   return false;
     363                 :          0 : }
     364                 :            : 
     365                 :          0 : QString QgsVirtualLayerDefinition::subsetString() const
     366                 :            : {
     367                 :          0 :   return mSubsetString;
     368                 :            : }
     369                 :            : 
     370                 :          0 : void QgsVirtualLayerDefinition::setSubsetString( const QString &subsetString )
     371                 :            : {
     372                 :          0 :   mSubsetString = subsetString;
     373                 :          0 : }

Generated by: LCOV version 1.14