QGIS API Documentation  2.99.0-Master (cd0ba91)
qgsproviderregistry.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsproviderregistry.cpp - Singleton class for
3  registering data providers.
4  -------------------
5  begin : Sat Jan 10 2004
6  copyright : (C) 2004 by Gary E.Sherman
7  email : sherman at mrcc.com
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 #include "qgsproviderregistry.h"
20 
21 #include <QString>
22 #include <QDir>
23 #include <QLibrary>
24 
25 #include "qgis.h"
26 #include "qgsdataprovider.h"
27 #include "qgslogger.h"
28 #include "qgsmessageoutput.h"
29 #include "qgsmessagelog.h"
30 #include "qgsprovidermetadata.h"
31 #include "qgsvectorlayer.h"
32 #include "qgsproject.h"
34 
35 
36 // typedefs for provider plugin functions of interest
37 typedef QString providerkey_t();
38 typedef QString description_t();
39 typedef bool isprovider_t();
40 typedef QString fileVectorFilters_t();
41 typedef void buildsupportedrasterfilefilter_t( QString &fileFiltersString );
42 typedef QString databaseDrivers_t();
43 typedef QString directoryDrivers_t();
44 typedef QString protocolDrivers_t();
45 //typedef int dataCapabilities_t();
46 //typedef QgsDataItem * dataItem_t(QString);
47 
48 static QgsProviderRegistry *sInstance = nullptr;
49 
51 {
52  if ( !sInstance )
53  {
54  static QMutex sMutex;
55  QMutexLocker locker( &sMutex );
56  if ( !sInstance )
57  {
58  sInstance = new QgsProviderRegistry( pluginPath );
59  }
60  }
61  return sInstance;
62 } // QgsProviderRegistry::instance
63 
64 
65 
66 QgsProviderRegistry::QgsProviderRegistry( const QString &pluginPath )
67 {
68  // At startup, examine the libs in the qgis/lib dir and store those that
69  // are a provider shared lib
70  // check all libs in the current plugin directory and get name and descriptions
71  //TODO figure out how to register and identify data source plugin for a specific
72  //TODO layer type
73 #if 0
74  char **argv = qApp->argv();
75  QString appDir = argv[0];
76  int bin = appDir.findRev( "/bin", -1, false );
77  QString baseDir = appDir.left( bin );
78  QString mLibraryDirectory = baseDir + "/lib";
79 #endif
80  mLibraryDirectory = pluginPath;
81  init();
82 }
83 
84 
85 void QgsProviderRegistry::init()
86 {
87  // add standard providers
88  mProviders[ QgsMemoryProvider::providerKey() ] = new QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription(), &QgsMemoryProvider::createProvider );
89 
90  mLibraryDirectory.setSorting( QDir::Name | QDir::IgnoreCase );
91  mLibraryDirectory.setFilter( QDir::Files | QDir::NoSymLinks );
92 
93 #if defined(Q_OS_WIN) || defined(__CYGWIN__)
94  mLibraryDirectory.setNameFilters( QStringList( "*.dll" ) );
95 #elif defined(ANDROID)
96  mLibraryDirectory.setNameFilters( QStringList( "*provider.so" ) );
97 #else
98  mLibraryDirectory.setNameFilters( QStringList( QStringLiteral( "*.so" ) ) );
99 #endif
100 
101  QgsDebugMsg( QString( "Checking %1 for provider plugins" ).arg( mLibraryDirectory.path() ) );
102 
103  if ( mLibraryDirectory.count() == 0 )
104  {
105  QString msg = QObject::tr( "No QGIS data provider plugins found in:\n%1\n" ).arg( mLibraryDirectory.path() );
106  msg += QObject::tr( "No vector layers can be loaded. Check your QGIS installation" );
107 
109  output->setTitle( QObject::tr( "No Data Providers" ) );
111  output->showMessage();
112  return;
113  }
114 
115  // provider file regex pattern, only files matching the pattern are loaded if the variable is defined
116  QString filePattern = getenv( "QGIS_PROVIDER_FILE" );
117  QRegExp fileRegexp;
118  if ( !filePattern.isEmpty() )
119  {
120  fileRegexp.setPattern( filePattern );
121  }
122 
123  Q_FOREACH ( const QFileInfo &fi, mLibraryDirectory.entryInfoList() )
124  {
125  if ( !fileRegexp.isEmpty() )
126  {
127  if ( fileRegexp.indexIn( fi.fileName() ) == -1 )
128  {
129  QgsDebugMsg( "provider " + fi.fileName() + " skipped because doesn't match pattern " + filePattern );
130  continue;
131  }
132  }
133 
134  QLibrary myLib( fi.filePath() );
135  if ( !myLib.load() )
136  {
137  QgsDebugMsg( QString( "Checking %1: ...invalid (lib not loadable): %2" ).arg( myLib.fileName(), myLib.errorString() ) );
138  continue;
139  }
140 
141  //MH: Added a further test to detect non-provider plugins linked to provider plugins.
142  //Only pure provider plugins have 'type' not defined
143  isprovider_t *hasType = reinterpret_cast< isprovider_t * >( cast_to_fptr( myLib.resolve( "type" ) ) );
144  if ( hasType )
145  {
146  QgsDebugMsg( QString( "Checking %1: ...invalid (has type method)" ).arg( myLib.fileName() ) );
147  continue;
148  }
149 
150  // get the description and the key for the provider plugin
151  isprovider_t *isProvider = reinterpret_cast< isprovider_t * >( cast_to_fptr( myLib.resolve( "isProvider" ) ) );
152  if ( !isProvider )
153  {
154  QgsDebugMsg( QString( "Checking %1: ...invalid (no isProvider method)" ).arg( myLib.fileName() ) );
155  continue;
156  }
157 
158  // check to see if this is a provider plugin
159  if ( !isProvider() )
160  {
161  QgsDebugMsg( QString( "Checking %1: ...invalid (not a provider)" ).arg( myLib.fileName() ) );
162  continue;
163  }
164 
165  // looks like a provider. get the key and description
166  description_t *pDesc = reinterpret_cast< description_t * >( cast_to_fptr( myLib.resolve( "description" ) ) );
167  if ( !pDesc )
168  {
169  QgsDebugMsg( QString( "Checking %1: ...invalid (no description method)" ).arg( myLib.fileName() ) );
170  continue;
171  }
172 
173  providerkey_t *pKey = reinterpret_cast< providerkey_t * >( cast_to_fptr( myLib.resolve( "providerKey" ) ) );
174  if ( !pKey )
175  {
176  QgsDebugMsg( QString( "Checking %1: ...invalid (no providerKey method)" ).arg( myLib.fileName() ) );
177  continue;
178  }
179 
180  // add this provider to the provider map
181  mProviders[pKey()] = new QgsProviderMetadata( pKey(), pDesc(), myLib.fileName() );
182 
183  // load database drivers
184  databaseDrivers_t *pDatabaseDrivers = reinterpret_cast< databaseDrivers_t * >( cast_to_fptr( myLib.resolve( "databaseDrivers" ) ) );
185  if ( pDatabaseDrivers )
186  {
187  mDatabaseDrivers = pDatabaseDrivers();
188  }
189 
190  // load directory drivers
191  directoryDrivers_t *pDirectoryDrivers = reinterpret_cast< directoryDrivers_t * >( cast_to_fptr( myLib.resolve( "directoryDrivers" ) ) );
192  if ( pDirectoryDrivers )
193  {
194  mDirectoryDrivers = pDirectoryDrivers();
195  }
196 
197  // load protocol drivers
198  protocolDrivers_t *pProtocolDrivers = reinterpret_cast< protocolDrivers_t * >( cast_to_fptr( myLib.resolve( "protocolDrivers" ) ) );
199  if ( pProtocolDrivers )
200  {
201  mProtocolDrivers = pProtocolDrivers();
202  }
203 
204  // now get vector file filters, if any
205  fileVectorFilters_t *pFileVectorFilters = reinterpret_cast< fileVectorFilters_t * >( cast_to_fptr( myLib.resolve( "fileVectorFilters" ) ) );
206  if ( pFileVectorFilters )
207  {
208  QString fileVectorFilters = pFileVectorFilters();
209 
210  if ( !fileVectorFilters.isEmpty() )
211  mVectorFileFilters += fileVectorFilters;
212 
213  QgsDebugMsg( QString( "Checking %1: ...loaded OK (%2 file filters)" ).arg( myLib.fileName() ).arg( fileVectorFilters.split( ";;" ).count() ) );
214  }
215 
216  // now get raster file filters, if any
217  // this replaces deprecated QgsRasterLayer::buildSupportedRasterFileFilter
219  reinterpret_cast< buildsupportedrasterfilefilter_t * >( cast_to_fptr( myLib.resolve( "buildSupportedRasterFileFilter" ) ) );
220  if ( pBuild )
221  {
222  QString fileRasterFilters;
223  pBuild( fileRasterFilters );
224 
225  QgsDebugMsg( "raster filters: " + fileRasterFilters );
226  if ( !fileRasterFilters.isEmpty() )
227  mRasterFileFilters += fileRasterFilters;
228 
229  QgsDebugMsg( QString( "Checking %1: ...loaded OK (%2 file filters)" ).arg( myLib.fileName() ).arg( fileRasterFilters.split( ";;" ).count() ) );
230  }
231  }
232 } // QgsProviderRegistry ctor
233 
234 
235 // typedef for the unload dataprovider function
237 
238 void QgsProviderRegistry::clean()
239 {
241 
242  Providers::const_iterator it = mProviders.begin();
243 
244  while ( it != mProviders.end() )
245  {
246  QgsDebugMsgLevel( QString( "cleanup:%1" ).arg( it->first ), 5 );
247  QString lib = it->second->library();
248  if ( !lib.isEmpty() )
249  {
250  QLibrary myLib( lib );
251  if ( myLib.isLoaded() )
252  {
253  cleanupProviderFunction_t *cleanupFunc = reinterpret_cast< cleanupProviderFunction_t * >( cast_to_fptr( myLib.resolve( "cleanupProvider" ) ) );
254  if ( cleanupFunc )
255  cleanupFunc();
256  }
257  }
258  delete it->second;
259  ++it;
260  }
261  mProviders.clear();
262 }
263 
265 {
266  clean();
267  if ( sInstance == this )
268  sInstance = nullptr;
269 }
270 
271 
280 static
281 QgsProviderMetadata *findMetadata_( QgsProviderRegistry::Providers const &metaData,
282  QString const &providerKey )
283 {
284  QgsProviderRegistry::Providers::const_iterator i =
285  metaData.find( providerKey );
286 
287  if ( i != metaData.end() )
288  {
289  return i->second;
290  }
291 
292  return nullptr;
293 } // findMetadata_
294 
295 
296 
297 QString QgsProviderRegistry::library( QString const &providerKey ) const
298 {
299  QgsProviderMetadata *md = findMetadata_( mProviders, providerKey );
300 
301  if ( md )
302  {
303  return md->library();
304  }
305 
306  return QString();
307 }
308 
309 
310 QString QgsProviderRegistry::pluginList( bool asHTML ) const
311 {
312  Providers::const_iterator it = mProviders.begin();
313 
314  if ( mProviders.empty() )
315  return QObject::tr( "No data provider plugins are available. No vector layers can be loaded" );
316 
317  QString list;
318 
319  if ( asHTML )
320  list += QLatin1String( "<ol>" );
321 
322  while ( it != mProviders.end() )
323  {
324  if ( asHTML )
325  list += QLatin1String( "<li>" );
326 
327  list += it->second->description();
328 
329  if ( asHTML )
330  list += QLatin1String( "<br></li>" );
331  else
332  list += '\n';
333 
334  ++it;
335  }
336 
337  if ( asHTML )
338  list += QLatin1String( "</ol>" );
339 
340  return list;
341 }
342 
344 {
345  mLibraryDirectory = path;
346  clean();
347  init();
348 }
349 
351 {
352  return mLibraryDirectory;
353 }
354 
355 
356 
357 // typedef for the QgsDataProvider class factory
358 typedef QgsDataProvider *classFactoryFunction_t( const QString * );
359 
360 
361 /* Copied from QgsVectorLayer::setDataProvider
362  * TODO: Make it work in the generic environment
363  *
364  * TODO: Is this class really the best place to put a data provider loader?
365  * It seems more sensible to provide the code in one place rather than
366  * in qgsrasterlayer, qgsvectorlayer, serversourceselect, etc.
367  */
368 QgsDataProvider *QgsProviderRegistry::createProvider( QString const &providerKey, QString const &dataSource )
369 {
370  // XXX should I check for and possibly delete any pre-existing providers?
371  // XXX How often will that scenario occur?
372 
373  const QgsProviderMetadata *metadata = providerMetadata( providerKey );
374  if ( !metadata )
375  {
376  QgsMessageLog::logMessage( QObject::tr( "Invalid data provider %1" ).arg( providerKey ) );
377  return nullptr;
378  }
379 
380  if ( metadata->createFunction() )
381  {
382  return metadata->createFunction()( dataSource );
383  }
384 
385  // load the plugin
386  QString lib = library( providerKey );
387 
388 #ifdef TESTPROVIDERLIB
389  const char *cLib = lib.toUtf8();
390 
391  // test code to help debug provider loading problems
392  // void *handle = dlopen(cLib, RTLD_LAZY);
393  void *handle = dlopen( cOgrLib, RTLD_LAZY | RTLD_GLOBAL );
394  if ( !handle )
395  {
396  QgsLogger::warning( "Error in dlopen" );
397  }
398  else
399  {
400  QgsDebugMsg( "dlopen succeeded" );
401  dlclose( handle );
402  }
403 
404 #endif
405  // load the data provider
406  QLibrary myLib( lib );
407 
408  QgsDebugMsg( "Library name is " + myLib.fileName() );
409  if ( !myLib.load() )
410  {
411  QgsMessageLog::logMessage( QObject::tr( "Failed to load %1: %2" ).arg( lib, myLib.errorString() ) );
412  return nullptr;
413  }
414 
415  classFactoryFunction_t *classFactory = reinterpret_cast< classFactoryFunction_t * >( cast_to_fptr( myLib.resolve( "classFactory" ) ) );
416  if ( !classFactory )
417  {
418  QgsDebugMsg( QString( "Failed to load %1: no classFactory method" ).arg( lib ) );
419  return nullptr;
420  }
421 
422  QgsDataProvider *dataProvider = classFactory( &dataSource );
423  if ( !dataProvider )
424  {
425  QgsMessageLog::logMessage( QObject::tr( "Unable to instantiate the data provider plugin %1" ).arg( lib ) );
426  myLib.unload();
427  return nullptr;
428  }
429 
430  QgsDebugMsg( QString( "Instantiated the data provider plugin: %1" ).arg( dataProvider->name() ) );
431  return dataProvider;
432 } // QgsProviderRegistry::setDataProvider
433 
434 int QgsProviderRegistry::providerCapabilities( const QString &providerKey ) const
435 {
436  std::unique_ptr< QLibrary > library( createProviderLibrary( providerKey ) );
437  if ( !library )
438  {
440  }
441 
442  dataCapabilities_t *dataCapabilities = reinterpret_cast< dataCapabilities_t *>( cast_to_fptr( library->resolve( "dataCapabilities" ) ) );
443  if ( !dataCapabilities )
444  {
446  }
447 
448  return dataCapabilities();
449 }
450 
451 // This should be QWidget, not QDialog
452 typedef QWidget *selectFactoryFunction_t( QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode widgetMode );
453 
454 QWidget *QgsProviderRegistry::createSelectionWidget( const QString &providerKey,
455  QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode widgetMode )
456 {
457  selectFactoryFunction_t *selectFactory =
458  reinterpret_cast< selectFactoryFunction_t * >( cast_to_fptr( function( providerKey, "selectWidget" ) ) );
459 
460  if ( !selectFactory )
461  return nullptr;
462 
463  return selectFactory( parent, fl, widgetMode );
464 }
465 
466 QFunctionPointer QgsProviderRegistry::function( QString const &providerKey,
467  QString const &functionName )
468 {
469  QString lib = library( providerKey );
470  if ( lib.isEmpty() )
471  return nullptr;
472 
473  QLibrary myLib( library( providerKey ) );
474 
475  QgsDebugMsg( "Library name is " + myLib.fileName() );
476 
477  if ( myLib.load() )
478  {
479  return myLib.resolve( functionName.toLatin1().data() );
480  }
481  else
482  {
483  QgsDebugMsg( "Cannot load library: " + myLib.errorString() );
484  return nullptr;
485  }
486 }
487 
488 QLibrary *QgsProviderRegistry::createProviderLibrary( QString const &providerKey ) const
489 {
490  QString lib = library( providerKey );
491  if ( lib.isEmpty() )
492  return nullptr;
493 
494  std::unique_ptr< QLibrary > myLib( new QLibrary( lib ) );
495 
496  QgsDebugMsg( "Library name is " + myLib->fileName() );
497 
498  if ( myLib->load() )
499  return myLib.release();
500 
501  QgsDebugMsg( "Cannot load library: " + myLib->errorString() );
502 
503  return nullptr;
504 }
505 
506 void QgsProviderRegistry::registerGuis( QWidget *parent )
507 {
508  typedef void registerGui_function( QWidget * parent );
509 
510  Q_FOREACH ( const QString &provider, providerList() )
511  {
512  registerGui_function *registerGui = reinterpret_cast< registerGui_function * >( cast_to_fptr( function( provider, "registerGui" ) ) );
513 
514  if ( !registerGui )
515  continue;
516 
517  registerGui( parent );
518  }
519 }
520 
522 {
523  return mVectorFileFilters;
524 }
525 
527 {
528  return mRasterFileFilters;
529 }
530 
532 {
533  return mDatabaseDrivers;
534 }
535 
537 {
538  return mDirectoryDrivers;
539 }
540 
542 {
543  return mProtocolDrivers;
544 }
545 
547 {
548  QStringList lst;
549  for ( Providers::const_iterator it = mProviders.begin(); it != mProviders.end(); ++it )
550  {
551  lst.append( it->first );
552  }
553  return lst;
554 }
555 
556 const QgsProviderMetadata *QgsProviderRegistry::providerMetadata( const QString &providerKey ) const
557 {
558  return findMetadata_( mProviders, providerKey );
559 }
WidgetMode
Different ways a source select dialog can be used (embedded is for the data source manager dialog) ...
QgsDataProvider * classFactoryFunction_t(const QString *)
QString databaseDrivers_t()
void cleanupProviderFunction_t()
virtual void setTitle(const QString &title)=0
set title for the messages
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
QString library() const
This returns the library file name.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:124
virtual QString protocolDrivers() const
Return a string containing the available protocol drivers.
QString library(const QString &providerKey) const
Return path for the library of the provider.
void registerGuis(QWidget *widget)
virtual QString databaseDrivers() const
Return a string containing the available database drivers.
QgsDataProvider * createProvider(const QString &providerKey, const QString &dataSource)
Creates a new instance of a provider.
QString directoryDrivers_t()
static QgsMessageOutput * createMessageOutput()
function that returns new class derived from QgsMessageOutput (don&#39;t forget to delete it then if show...
Abstract base class for spatial data provider implementations.
virtual QString name() const =0
Return a provider name.
QString description_t()
virtual QString fileVectorFilters() const
Return vector file filter string.
QWidget * createSelectionWidget(const QString &providerKey, QWidget *parent=nullptr, Qt::WindowFlags fl=Qt::WindowFlags(), QgsProviderRegistry::WidgetMode widgetMode=QgsProviderRegistry::WidgetMode::None)
Returns a new widget for selecting layers from a provider.
bool isprovider_t()
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:38
QLibrary * createProviderLibrary(const QString &providerKey) const
Returns a new QLibrary for the specified providerKey.
virtual void setMessage(const QString &message, MessageType msgType)=0
set message, it won&#39;t be displayed until
void setLibraryDirectory(const QDir &path)
Set library directory where to search for plugins.
CreateDataProviderFunction createFunction() const
Returns a pointer to the direct provider creation function, if supported by the provider.
const QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Return metadata of the provider or NULL if not found.
QString pluginList(bool asHtml=false) const
Return list of provider plugins found.
void removeAllMapLayers()
Removes all registered layers.
#define cast_to_fptr(f)
Definition: qgis.h:146
QString fileVectorFilters_t()
A registry / canonical manager of data providers.
int providerCapabilities(const QString &providerKey) const
Return the provider capabilities.
virtual void showMessage(bool blocking=true)=0
display the message to the user and deletes itself
static void logMessage(const QString &message, const QString &tag=QString(), MessageLevel level=QgsMessageLog::WARNING)
add a message to the instance (and create it if necessary)
QString providerkey_t()
Holds data provider key, description, and associated shared library file or function pointer informat...
virtual QString directoryDrivers() const
Return a string containing the available directory drivers.
std::map< QString, QgsProviderMetadata * > Providers
Open the given vector data source.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:383
QWidget * selectFactoryFunction_t(QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode widgetMode)
QFunctionPointer function(const QString &providerKey, const QString &functionName)
Get pointer to provider function.
virtual QString fileRasterFilters() const
Return raster file filter string.
void buildsupportedrasterfilefilter_t(QString &fileFiltersString)
QString protocolDrivers_t()
QStringList providerList() const
Return list of available providers by their keys.
Interface for showing messages from QGIS in GUI independent way.
int dataCapabilities_t()
QDir libraryDirectory() const
Returns the library directory where plugins are found.