QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsvirtuallayerdefinition.cpp
Go to the documentation of this file.
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 
22 #include "qgsvectorlayer.h"
23 #include "qgsvectordataprovider.h"
24 
26  : mFilePath( filePath )
27 {
28 }
29 
31 {
33 
34  def.setFilePath( url.toLocalFile() );
35 
36  // regexp for column name
37  const QString columnNameRx( QStringLiteral( "[a-zA-Z_\x80-\xFF][a-zA-Z0-9_\x80-\xFF]*" ) );
38 
40 
41  int layerIdx = 0;
42 
43  const QList<QPair<QString, QString> > items = QUrlQuery( url ).queryItems( QUrl::FullyEncoded );
44  for ( int i = 0; i < items.size(); i++ )
45  {
46  QString key = items.at( i ).first;
47  QString value = items.at( i ).second;
48  if ( key == QLatin1String( "layer_ref" ) )
49  {
50  layerIdx++;
51  // layer id, with optional layer_name
52  int pos = value.indexOf( ':' );
53  QString layerId, vlayerName;
54  if ( pos == -1 )
55  {
56  layerId = value;
57  vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
58  }
59  else
60  {
61  layerId = value.left( pos );
62  vlayerName = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
63  }
64  // add the layer to the list
65  def.addSource( vlayerName, layerId );
66  }
67  else if ( key == QLatin1String( "layer" ) )
68  {
69  layerIdx++;
70  // syntax: layer=provider:url_encoded_source_URI(:name(:encoding)?)?
71  int pos = value.indexOf( ':' );
72  if ( pos != -1 )
73  {
74  QString providerKey, source, vlayerName, encoding = QStringLiteral( "UTF-8" );
75 
76  providerKey = value.left( pos );
77  int pos2 = value.indexOf( ':', pos + 1 );
78  if ( pos2 - pos == 2 )
79  pos2 = value.indexOf( ':', pos + 3 );
80  if ( pos2 != -1 )
81  {
82  source = QUrl::fromPercentEncoding( value.mid( pos + 1, pos2 - pos - 1 ).toUtf8() );
83  int pos3 = value.indexOf( ':', pos2 + 1 );
84  if ( pos3 != -1 )
85  {
86  vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1, pos3 - pos2 - 1 ).toUtf8() );
87  encoding = value.mid( pos3 + 1 );
88  }
89  else
90  {
91  vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1 ).toUtf8() );
92  }
93  }
94  else
95  {
96  source = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
97  vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
98  }
99 
100  def.addSource( vlayerName, source, providerKey, encoding );
101  }
102  }
103  else if ( key == QLatin1String( "geometry" ) )
104  {
105  // geometry field definition, optional
106  // geometry_column(:wkb_type:srid)?
107  QRegExp reGeom( "(" + columnNameRx + ")(?::([a-zA-Z0-9]+):(\\d+))?" );
108  int pos = reGeom.indexIn( value );
109  if ( pos >= 0 )
110  {
111  def.setGeometryField( reGeom.cap( 1 ) );
112  if ( reGeom.captureCount() > 1 )
113  {
114  // not used by the spatialite provider for now ...
115  QgsWkbTypes::Type wkbType = QgsWkbTypes::parseType( reGeom.cap( 2 ) );
116  if ( wkbType == QgsWkbTypes::Unknown )
117  {
118  wkbType = static_cast<QgsWkbTypes::Type>( reGeom.cap( 2 ).toLong() );
119  }
120  def.setGeometryWkbType( wkbType );
121  def.setGeometrySrid( reGeom.cap( 3 ).toLong() );
122  }
123  }
124  }
125  else if ( key == QLatin1String( "nogeometry" ) )
126  {
128  }
129  else if ( key == QLatin1String( "uid" ) )
130  {
131  def.setUid( value );
132  }
133  else if ( key == QLatin1String( "query" ) )
134  {
135  // url encoded query
136  def.setQuery( QUrl::fromPercentEncoding( value.toUtf8() ) );
137  }
138  else if ( key == QLatin1String( "field" ) )
139  {
140  // field_name:type (int, real, text)
141  QRegExp reField( "(" + columnNameRx + "):(int|real|text)" );
142  int pos = reField.indexIn( value );
143  if ( pos >= 0 )
144  {
145  QString fieldName( reField.cap( 1 ) );
146  QString fieldType( reField.cap( 2 ) );
147  if ( fieldType == QLatin1String( "int" ) )
148  {
149  fields.append( QgsField( fieldName, QVariant::Int, fieldType ) );
150  }
151  else if ( fieldType == QLatin1String( "real" ) )
152  {
153  fields.append( QgsField( fieldName, QVariant::Double, fieldType ) );
154  }
155  if ( fieldType == QLatin1String( "text" ) )
156  {
157  fields.append( QgsField( fieldName, QVariant::String, fieldType ) );
158  }
159  }
160  }
161  else if ( key == QLatin1String( "lazy" ) )
162  {
163  def.setLazy( true );
164  }
165  }
166  def.setFields( fields );
167 
168  return def;
169 }
170 
171 // Mega ewwww. all this is taken from Qt's QUrl::addEncodedQueryItem compatibility helper.
172 // (I can't see any way to port the below code to NOT require this without breaking
173 // existing projects.)
174 
175 inline char toHexUpper( uint value ) noexcept
176 {
177  return "0123456789ABCDEF"[value & 0xF];
178 }
179 
180 static inline ushort encodeNibble( ushort c )
181 {
182  return ushort( toHexUpper( c ) );
183 }
184 
185 bool qt_is_ascii( const char *&ptr, const char *end ) noexcept
186 {
187  while ( ptr + 4 <= end )
188  {
189  quint32 data = qFromUnaligned<quint32>( ptr );
190  if ( data &= 0x80808080U )
191  {
192 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
193  uint idx = qCountLeadingZeroBits( data );
194 #else
195  uint idx = qCountTrailingZeroBits( data );
196 #endif
197  ptr += idx / 8;
198  return false;
199  }
200  ptr += 4;
201  }
202  while ( ptr != end )
203  {
204  if ( quint8( *ptr ) & 0x80 )
205  return false;
206  ++ptr;
207  }
208  return true;
209 }
210 
211 QString fromEncodedComponent_helper( const QByteArray &ba )
212 {
213  if ( ba.isNull() )
214  return QString();
215  // scan ba for anything above or equal to 0x80
216  // control points below 0x20 are fine in QString
217  const char *in = ba.constData();
218  const char *const end = ba.constEnd();
219  if ( qt_is_ascii( in, end ) )
220  {
221  // no non-ASCII found, we're safe to convert to QString
222  return QString::fromLatin1( ba, ba.size() );
223  }
224  // we found something that we need to encode
225  QByteArray intermediate = ba;
226  intermediate.resize( ba.size() * 3 - ( in - ba.constData() ) );
227  uchar *out = reinterpret_cast<uchar *>( intermediate.data() + ( in - ba.constData() ) );
228  for ( ; in < end; ++in )
229  {
230  if ( *in & 0x80 )
231  {
232  // encode
233  *out++ = '%';
234  *out++ = encodeNibble( uchar( *in ) >> 4 );
235  *out++ = encodeNibble( uchar( *in ) & 0xf );
236  }
237  else
238  {
239  // keep
240  *out++ = uchar( *in );
241  }
242  }
243  // now it's safe to call fromLatin1
244  return QString::fromLatin1( intermediate, out - reinterpret_cast<uchar *>( intermediate.data() ) );
245 }
246 
248 {
249  QUrl url;
250  if ( !filePath().isEmpty() )
251  url = QUrl::fromLocalFile( filePath() );
252 
253  QUrlQuery urlQuery( url );
254 
255  const auto constSourceLayers = sourceLayers();
256  for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
257  {
258  if ( l.isReferenced() )
259  urlQuery.addQueryItem( QStringLiteral( "layer_ref" ), QStringLiteral( "%1:%2" ).arg( l.reference(), l.name() ) );
260  else
261  // if you can find a way to port this away from fromEncodedComponent_helper without breaking existing projects,
262  // please do so... this is GROSS!
263  urlQuery.addQueryItem( fromEncodedComponent_helper( "layer" ),
264  fromEncodedComponent_helper( QStringLiteral( "%1:%4:%2:%3" ) // the order is important, since the 4th argument may contain '%2' as well
265  .arg( l.provider(),
266  QString( QUrl::toPercentEncoding( l.name() ) ),
267  l.encoding(),
268  QString( QUrl::toPercentEncoding( l.source() ) ) ).toUtf8() ) );
269  }
270 
271  if ( !query().isEmpty() )
272  {
273  urlQuery.addQueryItem( QStringLiteral( "query" ), query() );
274  }
275 
276  if ( !uid().isEmpty() )
277  urlQuery.addQueryItem( QStringLiteral( "uid" ), uid() );
278 
280  urlQuery.addQueryItem( QStringLiteral( "nogeometry" ), QString() );
281  else if ( !geometryField().isEmpty() )
282  {
283  if ( hasDefinedGeometry() )
284  urlQuery.addQueryItem( QStringLiteral( "geometry" ), QStringLiteral( "%1:%2:%3" ).arg( geometryField() ). arg( geometryWkbType() ).arg( geometrySrid() ).toUtf8() );
285  else
286  urlQuery.addQueryItem( QStringLiteral( "geometry" ), geometryField() );
287  }
288 
289  const auto constFields = fields();
290  for ( const QgsField &f : constFields )
291  {
292  if ( f.type() == QVariant::Int )
293  urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":int" );
294  else if ( f.type() == QVariant::Double )
295  urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":real" );
296  else if ( f.type() == QVariant::String )
297  urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":text" );
298  }
299 
300  if ( isLazy() )
301  {
302  urlQuery.addQueryItem( QStringLiteral( "lazy" ), QString() );
303  }
304 
305  url.setQuery( urlQuery );
306 
307  return url;
308 }
309 
311 {
312  return QString( toUrl().toEncoded() );
313 }
314 
315 void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &ref )
316 {
317  mSourceLayers.append( SourceLayer( name, ref ) );
318 }
319 
320 void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &source, const QString &provider, const QString &encoding )
321 {
322  mSourceLayers.append( SourceLayer( name, source, provider, encoding ) );
323 }
324 
325 bool QgsVirtualLayerDefinition::hasSourceLayer( const QString &name ) const
326 {
327  const auto constSourceLayers = sourceLayers();
328  for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
329  {
330  if ( l.name() == name )
331  {
332  return true;
333  }
334  }
335  return false;
336 }
337 
339 {
340  const auto constSourceLayers = sourceLayers();
341  for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
342  {
343  if ( l.isReferenced() )
344  {
345  return true;
346  }
347  }
348  return false;
349 }
QgsVirtualLayerDefinition::setGeometryWkbType
void setGeometryWkbType(QgsWkbTypes::Type t)
Sets the type of the geometry.
Definition: qgsvirtuallayerdefinition.h:176
qgsvirtuallayerdefinition.h
QgsVirtualLayerDefinition::setGeometrySrid
void setGeometrySrid(long srid)
Sets the SRID of the geometry.
Definition: qgsvirtuallayerdefinition.h:181
QgsVirtualLayerDefinition::setQuery
void setQuery(const QString &query)
Sets the SQL query.
Definition: qgsvirtuallayerdefinition.h:134
QgsVirtualLayerDefinition::geometryField
QString geometryField() const
Gets the name of the geometry field. Empty if no geometry field.
Definition: qgsvirtuallayerdefinition.h:165
QgsVirtualLayerDefinition::setLazy
void setLazy(bool lazy)
Sets the lazy mode.
Definition: qgsvirtuallayerdefinition.h:154
QgsVirtualLayerDefinition::setUid
void setUid(const QString &uid)
Sets the name of the field with unique identifiers.
Definition: qgsvirtuallayerdefinition.h:144
QgsVirtualLayerDefinition::sourceLayers
const QgsVirtualLayerDefinition::SourceLayers & sourceLayers() const
Gets access to the source layers.
Definition: qgsvirtuallayerdefinition.h:129
QgsFields
Definition: qgsfields.h:44
QgsVirtualLayerDefinition::SourceLayer
Definition: qgsvirtuallayerdefinition.h:52
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QgsVirtualLayerDefinition::geometrySrid
long geometrySrid() const
Gets the SRID of the geometry.
Definition: qgsvirtuallayerdefinition.h:179
QgsFields::append
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
QgsWkbTypes::parseType
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
QgsVirtualLayerDefinition::setFields
void setFields(const QgsFields &fields)
Sets field definitions.
Definition: qgsvirtuallayerdefinition.h:186
QgsVirtualLayerDefinition::addSource
void addSource(const QString &name, const QString &ref)
Add a live layer source layer.
Definition: qgsvirtuallayerdefinition.cpp:315
QgsWkbTypes::Unknown
@ Unknown
Definition: qgswkbtypes.h:70
QgsVirtualLayerDefinition::QgsVirtualLayerDefinition
QgsVirtualLayerDefinition(const QString &filePath="")
Constructor with an optional file path.
Definition: qgsvirtuallayerdefinition.cpp:25
qgsvectordataprovider.h
QgsVirtualLayerDefinition::setGeometryField
void setGeometryField(const QString &geometryField)
Sets the name of the geometry field.
Definition: qgsvirtuallayerdefinition.h:167
QgsVirtualLayerDefinition::fromUrl
static QgsVirtualLayerDefinition fromUrl(const QUrl &url)
Constructor to build a definition from a QUrl The path part of the URL is extracted as well as the fo...
Definition: qgsvirtuallayerdefinition.cpp:30
toHexUpper
char toHexUpper(uint value) noexcept
Definition: qgsvirtuallayerdefinition.cpp:175
fromEncodedComponent_helper
QString fromEncodedComponent_helper(const QByteArray &ba)
Definition: qgsvirtuallayerdefinition.cpp:211
QgsVirtualLayerDefinition::uid
QString uid() const
Gets the name of the field with unique identifiers.
Definition: qgsvirtuallayerdefinition.h:142
QgsVirtualLayerDefinition::hasReferencedLayers
bool hasReferencedLayers() const
Convenience method to test whether the definition has referenced (live) layers.
Definition: qgsvirtuallayerdefinition.cpp:338
qgsvectorlayer.h
QgsVirtualLayerDefinition::isLazy
bool isLazy() const
Returns the lazy mode.
Definition: qgsvirtuallayerdefinition.h:162
QgsVirtualLayerDefinition::query
QString query() const
Gets the SQL query.
Definition: qgsvirtuallayerdefinition.h:132
QgsVirtualLayerDefinition::setFilePath
void setFilePath(const QString &filePath)
Sets the file path.
Definition: qgsvirtuallayerdefinition.h:139
QgsWkbTypes::NoGeometry
@ NoGeometry
Definition: qgswkbtypes.h:84
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsVirtualLayerDefinition
Definition: qgsvirtuallayerdefinition.h:31
QgsVirtualLayerDefinition::hasDefinedGeometry
bool hasDefinedGeometry() const
Convenient method to test if the geometry is defined (not NoGeometry and not Unknown)
Definition: qgsvirtuallayerdefinition.h:195
QgsVirtualLayerDefinition::toString
QString toString() const
Convert into a QString that can be read by the virtual layer provider.
Definition: qgsvirtuallayerdefinition.cpp:310
QgsVirtualLayerDefinition::geometryWkbType
QgsWkbTypes::Type geometryWkbType() const
Gets the type of the geometry QgsWkbTypes::NoGeometry to hide any geometry QgsWkbTypes::Unknown for u...
Definition: qgsvirtuallayerdefinition.h:174
QgsFields::at
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsVirtualLayerDefinition::fields
QgsFields fields() const
Gets field definitions.
Definition: qgsvirtuallayerdefinition.h:184
QgsVirtualLayerDefinition::hasSourceLayer
bool hasSourceLayer(const QString &name) const
Convenience method to test if a given source layer is part of the definition.
Definition: qgsvirtuallayerdefinition.cpp:325
QgsVirtualLayerDefinition::toUrl
QUrl toUrl() const
Convert the definition into a QUrl.
Definition: qgsvirtuallayerdefinition.cpp:247
QgsVirtualLayerDefinition::filePath
QString filePath() const
Gets the file path. May be empty.
Definition: qgsvirtuallayerdefinition.h:137
qt_is_ascii
bool qt_is_ascii(const char *&ptr, const char *end) noexcept
Definition: qgsvirtuallayerdefinition.cpp:185
QgsField
Definition: qgsfield.h:49