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