QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 "qgsmaplayerregistry.h"
33 
34 
35 // typedefs for provider plugin functions of interest
38 typedef bool isprovider_t();
40 typedef void buildsupportedrasterfilefilter_t( QString & theFileFiltersString );
44 //typedef int dataCapabilities_t();
45 //typedef QgsDataItem * dataItem_t(QString);
46 
47 
48 
50 {
51  static QgsProviderRegistry* sInstance( new QgsProviderRegistry( pluginPath ) );
52  return sInstance;
53 } // QgsProviderRegistry::instance
54 
55 
56 
57 QgsProviderRegistry::QgsProviderRegistry( const QString& pluginPath )
58 {
59  // At startup, examine the libs in the qgis/lib dir and store those that
60  // are a provider shared lib
61  // check all libs in the current plugin directory and get name and descriptions
62  //TODO figure out how to register and identify data source plugin for a specific
63  //TODO layer type
64 #if 0
65  char **argv = qApp->argv();
66  QString appDir = argv[0];
67  int bin = appDir.findRev( "/bin", -1, false );
68  QString baseDir = appDir.left( bin );
69  QString mLibraryDirectory = baseDir + "/lib";
70 #endif
71  mLibraryDirectory = pluginPath;
72  init();
73 }
74 
75 
76 void QgsProviderRegistry::init()
77 {
78  mLibraryDirectory.setSorting( QDir::Name | QDir::IgnoreCase );
79  mLibraryDirectory.setFilter( QDir::Files | QDir::NoSymLinks );
80 
81 #if defined(Q_OS_WIN) || defined(__CYGWIN__)
82  mLibraryDirectory.setNameFilters( QStringList( "*.dll" ) );
83 #elif defined(ANDROID)
84  mLibraryDirectory.setNameFilters( QStringList( "*provider.so" ) );
85 #else
86  mLibraryDirectory.setNameFilters( QStringList( "*.so" ) );
87 #endif
88 
89  QgsDebugMsg( QString( "Checking %1 for provider plugins" ).arg( mLibraryDirectory.path() ) );
90 
91  if ( mLibraryDirectory.count() == 0 )
92  {
93  QString msg = QObject::tr( "No QGIS data provider plugins found in:\n%1\n" ).arg( mLibraryDirectory.path() );
94  msg += QObject::tr( "No vector layers can be loaded. Check your QGIS installation" );
95 
97  output->setTitle( QObject::tr( "No Data Providers" ) );
99  output->showMessage();
100  return;
101  }
102 
103  // provider file regex pattern, only files matching the pattern are loaded if the variable is defined
104  QString filePattern = getenv( "QGIS_PROVIDER_FILE" );
105  QRegExp fileRegexp;
106  if ( !filePattern.isEmpty() )
107  {
108  fileRegexp.setPattern( filePattern );
109  }
110 
111  Q_FOREACH ( const QFileInfo& fi, mLibraryDirectory.entryInfoList() )
112  {
113  if ( !fileRegexp.isEmpty() )
114  {
115  if ( fileRegexp.indexIn( fi.fileName() ) == -1 )
116  {
117  QgsDebugMsg( "provider " + fi.fileName() + " skipped because doesn't match pattern " + filePattern );
118  continue;
119  }
120  }
121 
122  QLibrary myLib( fi.filePath() );
123  if ( !myLib.load() )
124  {
125  QgsDebugMsg( QString( "Checking %1: ...invalid (lib not loadable): %2" ).arg( myLib.fileName(), myLib.errorString() ) );
126  continue;
127  }
128 
129  //MH: Added a further test to detect non-provider plugins linked to provider plugins.
130  //Only pure provider plugins have 'type' not defined
131  isprovider_t *hasType = reinterpret_cast< isprovider_t * >( cast_to_fptr( myLib.resolve( "type" ) ) );
132  if ( hasType )
133  {
134  QgsDebugMsg( QString( "Checking %1: ...invalid (has type method)" ).arg( myLib.fileName() ) );
135  continue;
136  }
137 
138  // get the description and the key for the provider plugin
139  isprovider_t *isProvider = reinterpret_cast< isprovider_t * >( cast_to_fptr( myLib.resolve( "isProvider" ) ) );
140  if ( !isProvider )
141  {
142  QgsDebugMsg( QString( "Checking %1: ...invalid (no isProvider method)" ).arg( myLib.fileName() ) );
143  continue;
144  }
145 
146  // check to see if this is a provider plugin
147  if ( !isProvider() )
148  {
149  QgsDebugMsg( QString( "Checking %1: ...invalid (not a provider)" ).arg( myLib.fileName() ) );
150  continue;
151  }
152 
153  // looks like a provider. get the key and description
154  description_t *pDesc = reinterpret_cast< description_t * >( cast_to_fptr( myLib.resolve( "description" ) ) );
155  if ( !pDesc )
156  {
157  QgsDebugMsg( QString( "Checking %1: ...invalid (no description method)" ).arg( myLib.fileName() ) );
158  continue;
159  }
160 
161  providerkey_t *pKey = reinterpret_cast< providerkey_t * >( cast_to_fptr( myLib.resolve( "providerKey" ) ) );
162  if ( !pKey )
163  {
164  QgsDebugMsg( QString( "Checking %1: ...invalid (no providerKey method)" ).arg( myLib.fileName() ) );
165  continue;
166  }
167 
168  // add this provider to the provider map
169  mProviders[pKey()] = new QgsProviderMetadata( pKey(), pDesc(), myLib.fileName() );
170 
171  // load database drivers
172  databaseDrivers_t *pDatabaseDrivers = reinterpret_cast< databaseDrivers_t * >( cast_to_fptr( myLib.resolve( "databaseDrivers" ) ) );
173  if ( pDatabaseDrivers )
174  {
175  mDatabaseDrivers = pDatabaseDrivers();
176  }
177 
178  // load directory drivers
179  directoryDrivers_t *pDirectoryDrivers = reinterpret_cast< directoryDrivers_t * >( cast_to_fptr( myLib.resolve( "directoryDrivers" ) ) );
180  if ( pDirectoryDrivers )
181  {
182  mDirectoryDrivers = pDirectoryDrivers();
183  }
184 
185  // load protocol drivers
186  protocolDrivers_t *pProtocolDrivers = reinterpret_cast< protocolDrivers_t * >( cast_to_fptr( myLib.resolve( "protocolDrivers" ) ) );
187  if ( pProtocolDrivers )
188  {
189  mProtocolDrivers = pProtocolDrivers();
190  }
191 
192  // now get vector file filters, if any
193  fileVectorFilters_t *pFileVectorFilters = reinterpret_cast< fileVectorFilters_t * >( cast_to_fptr( myLib.resolve( "fileVectorFilters" ) ) );
194  if ( pFileVectorFilters )
195  {
196  QString fileVectorFilters = pFileVectorFilters();
197 
198  if ( !fileVectorFilters.isEmpty() )
199  mVectorFileFilters += fileVectorFilters;
200 
201  QgsDebugMsg( QString( "Checking %1: ...loaded ok (%2 file filters)" ).arg( myLib.fileName() ).arg( fileVectorFilters.split( ";;" ).count() ) );
202  }
203 
204  // now get raster file filters, if any
205  // this replaces deprecated QgsRasterLayer::buildSupportedRasterFileFilter
207  reinterpret_cast< buildsupportedrasterfilefilter_t * >( cast_to_fptr( myLib.resolve( "buildSupportedRasterFileFilter" ) ) );
208  if ( pBuild )
209  {
211  pBuild( fileRasterFilters );
212 
213  QgsDebugMsg( "raster filters: " + fileRasterFilters );
214  if ( !fileRasterFilters.isEmpty() )
215  mRasterFileFilters += fileRasterFilters;
216 
217  QgsDebugMsg( QString( "Checking %1: ...loaded ok (%2 file filters)" ).arg( myLib.fileName() ).arg( fileRasterFilters.split( ";;" ).count() ) );
218  }
219  }
220 } // QgsProviderRegistry ctor
221 
222 
223 // typedef for the unload dataprovider function
225 
226 void QgsProviderRegistry::clean()
227 {
229 
230  Providers::const_iterator it = mProviders.begin();
231 
232  while ( it != mProviders.end() )
233  {
234  QgsDebugMsg( QString( "cleanup:%1" ).arg( it->first ) );
235  QString lib = it->second->library();
236  QLibrary myLib( lib );
237  if ( myLib.isLoaded() )
238  {
239  cleanupProviderFunction_t* cleanupFunc = reinterpret_cast< cleanupProviderFunction_t* >( cast_to_fptr( myLib.resolve( "cleanupProvider" ) ) );
240  if ( cleanupFunc )
241  cleanupFunc();
242  }
243  delete it->second;
244  ++it;
245  }
246 }
247 
249 {
250  clean();
251 }
252 
253 
261 static
263  QString const & providerKey )
264 {
265  QgsProviderRegistry::Providers::const_iterator i =
266  metaData.find( providerKey );
267 
268  if ( i != metaData.end() )
269  {
270  return i->second;
271  }
272 
273  return nullptr;
274 } // findMetadata_
275 
276 
277 
278 QString QgsProviderRegistry::library( QString const & providerKey ) const
279 {
280  QgsProviderMetadata * md = findMetadata_( mProviders, providerKey );
281 
282  if ( md )
283  {
284  return md->library();
285  }
286 
287  return QString();
288 }
289 
290 
292 {
293  Providers::const_iterator it = mProviders.begin();
294 
295  if ( mProviders.empty() )
296  return QObject::tr( "No data provider plugins are available. No vector layers can be loaded" );
297 
298  QString list;
299 
300  if ( asHTML )
301  list += "<ol>";
302 
303  while ( it != mProviders.end() )
304  {
305  if ( asHTML )
306  list += "<li>";
307 
308  list += it->second->description();
309 
310  if ( asHTML )
311  list + "<br></li>";
312  else
313  list += '\n';
314 
315  ++it;
316  }
317 
318  if ( asHTML )
319  list += "</ol>";
320 
321  return list;
322 }
323 
325 {
326  mLibraryDirectory = path;
327  clean();
328  init();
329 }
330 
332 {
333  return mLibraryDirectory;
334 }
335 
336 
337 
338 // typedef for the QgsDataProvider class factory
340 
341 
349 QgsDataProvider *QgsProviderRegistry::provider( QString const & providerKey, QString const & dataSource )
350 {
351  // XXX should I check for and possibly delete any pre-existing providers?
352  // XXX How often will that scenario occur?
353 
354  // load the plugin
355  QString lib = library( providerKey );
356 
357 #ifdef TESTPROVIDERLIB
358  const char *cLib = lib.toUtf8();
359 
360  // test code to help debug provider loading problems
361  // void *handle = dlopen(cLib, RTLD_LAZY);
362  void *handle = dlopen( cOgrLib, RTLD_LAZY | RTLD_GLOBAL );
363  if ( !handle )
364  {
365  QgsLogger::warning( "Error in dlopen" );
366  }
367  else
368  {
369  QgsDebugMsg( "dlopen succeeded" );
370  dlclose( handle );
371  }
372 
373 #endif
374  // load the data provider
375  QLibrary myLib( lib );
376 
377  QgsDebugMsg( "Library name is " + myLib.fileName() );
378  if ( !myLib.load() )
379  {
380  QgsMessageLog::logMessage( QObject::tr( "Failed to load %1: %2" ).arg( lib, myLib.errorString() ) );
381  return nullptr;
382  }
383 
384  classFactoryFunction_t *classFactory = reinterpret_cast< classFactoryFunction_t * >( cast_to_fptr( myLib.resolve( "classFactory" ) ) );
385  if ( !classFactory )
386  {
387  QgsDebugMsg( QString( "Failed to load %1: no classFactory method" ).arg( lib ) );
388  return nullptr;
389  }
390 
391  QgsDataProvider *dataProvider = classFactory( &dataSource );
392  if ( !dataProvider )
393  {
394  QgsMessageLog::logMessage( QObject::tr( "Unable to instantiate the data provider plugin %1" ).arg( lib ) );
395  myLib.unload();
396  return nullptr;
397  }
398 
399  QgsDebugMsg( QString( "Instantiated the data provider plugin: %1" ).arg( dataProvider->name() ) );
400  return dataProvider;
401 } // QgsProviderRegistry::setDataProvider
402 
403 int QgsProviderRegistry::providerCapabilities( const QString &providerKey ) const
404 {
405  QLibrary *library = providerLibrary( providerKey );
406  if ( !library )
407  {
409  }
410 
411  dataCapabilities_t * dataCapabilities = reinterpret_cast< dataCapabilities_t *>( cast_to_fptr( library->resolve( "dataCapabilities" ) ) );
412  if ( !dataCapabilities )
413  {
415  }
416 
417  return dataCapabilities();
418 }
419 
420 // This should be QWidget, not QDialog
422 
424  QWidget * parent, const Qt::WindowFlags& fl )
425 {
426  selectFactoryFunction_t * selectFactory =
427  reinterpret_cast< selectFactoryFunction_t * >( cast_to_fptr( function( providerKey, "selectWidget" ) ) );
428 
429  if ( !selectFactory )
430  return nullptr;
431 
432  return selectFactory( parent, fl );
433 }
434 
435 #if QT_VERSION >= 0x050000
436 QFunctionPointer QgsProviderRegistry::function( QString const & providerKey,
437  QString const & functionName )
438 {
439  QLibrary myLib( library( providerKey ) );
440 
441  QgsDebugMsg( "Library name is " + myLib.fileName() );
442 
443  if ( myLib.load() )
444  {
445  return myLib.resolve( functionName.toAscii().data() );
446  }
447  else
448  {
449  QgsDebugMsg( "Cannot load library: " + myLib.errorString() );
450  return 0;
451  }
452 }
453 #else
454 void *QgsProviderRegistry::function( QString const & providerKey,
455  QString const & functionName )
456 {
457  QLibrary myLib( library( providerKey ) );
458 
459  QgsDebugMsg( "Library name is " + myLib.fileName() );
460 
461  if ( myLib.load() )
462  {
463  return myLib.resolve( functionName.toAscii().data() );
464  }
465  else
466  {
467  QgsDebugMsg( "Cannot load library: " + myLib.errorString() );
468  return nullptr;
469  }
470 }
471 #endif
472 
474 {
475  QLibrary *myLib = new QLibrary( library( providerKey ) );
476 
477  QgsDebugMsg( "Library name is " + myLib->fileName() );
478 
479  if ( myLib->load() )
480  return myLib;
481 
482  QgsDebugMsg( "Cannot load library: " + myLib->errorString() );
483 
484  delete myLib;
485 
486  return nullptr;
487 }
488 
490 {
491  typedef void registerGui_function( QWidget * parent );
492 
493  Q_FOREACH ( const QString &provider, providerList() )
494  {
495  registerGui_function *registerGui = reinterpret_cast< registerGui_function * >( cast_to_fptr( function( provider, "registerGui" ) ) );
496 
497  if ( !registerGui )
498  continue;
499 
500  registerGui( parent );
501  }
502 }
503 
505 {
506  return mVectorFileFilters;
507 }
508 
510 {
511  return mRasterFileFilters;
512 }
513 
515 {
516  return mDatabaseDrivers;
517 }
518 
520 {
521  return mDirectoryDrivers;
522 }
523 
525 {
526  return mProtocolDrivers;
527 }
528 
530 {
531  QStringList lst;
532  for ( Providers::const_iterator it = mProviders.begin(); it != mProviders.end(); ++it )
533  {
534  lst.append( it->first );
535  }
536  return lst;
537 }
538 
540 {
541  return findMetadata_( mProviders, providerKey );
542 }
static QgsProviderRegistry * instance(const QString &pluginPath=QString::null)
Means of accessing canonical single instance.
QgsDataProvider * classFactoryFunction_t(const QString *)
QString databaseDrivers_t()
void cleanupProviderFunction_t()
static QgsProviderMetadata * findMetadata_(QgsProviderRegistry::Providers const &metaData, QString const &providerKey)
Convenience function for finding any existing data providers that match "providerKey".
void setNameFilters(const QStringList &nameFilters)
int findRev(QChar c, int i, bool cs) const
bool isEmpty() const
const QDir & libraryDirectory() const
Return library directory where plugins are found.
virtual void setTitle(const QString &title)=0
set title for the messages
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QString library() const
This returns the library file name.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
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.
QLibrary * providerLibrary(const QString &providerKey) const
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.
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.
void removeAllMapLayers()
Removes all registered layers.
virtual ~QgsProviderRegistry()
Virtual dectructor.
bool unload()
void setSorting(QFlags< QDir::SortFlag > sort)
QString tr(const char *sourceText, const char *disambiguation, int n)
QWidget * selectFactoryFunction_t(QWidget *parent, Qt::WindowFlags fl)
virtual QString name() const =0
Return a provider name.
QString description_t()
uint count() const
virtual QString fileVectorFilters() const
Return vector file filter string.
QString filePath() const
void setPattern(const QString &pattern)
int indexIn(const QString &str, int offset, CaretMode caretMode) const
bool isprovider_t()
void setFilter(QFlags< QDir::Filter > filters)
int count(const T &value) const
void append(const T &value)
QString path() const
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
QString fileName() const
QWidget * selectWidget(const QString &providerKey, QWidget *parent=nullptr, const Qt::WindowFlags &fl=Qt::WindowFlags())
Returns a widget for selecting layers from a provider.
QFileInfoList entryInfoList(QFlags< QDir::Filter > filters, QFlags< QDir::SortFlag > sort) const
bool isEmpty() const
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
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.
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 buildsupportedrasterfilefilter_t(QString &theFileFiltersString)
bool load()
QString fileVectorFilters_t()
std::map< QString, QgsProviderMetadata * > Providers
Open the given vector data source.
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
QString providerkey_t()
Holds data provider key, description, and associated shared library file information.
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
void * resolve(const char *symbol)
virtual QString directoryDrivers() const
Return a string containing the available directory drivers.
virtual QString fileRasterFilters() const
Return raster file filter string.
char * data()
QString left(int n) const
void * function(const QString &providerKey, const QString &functionName)
Get pointer to provider function.
typedef WindowFlags
void(*)() cast_to_fptr(void *p)
Definition: qgis.h:272
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()
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QByteArray toAscii() const
QString errorString() const
QByteArray toUtf8() const