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