QGIS API Documentation  2.14.0-Essen
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  QListIterator<QFileInfo> it( mLibraryDirectory.entryInfoList() );
112  while ( it.hasNext() )
113  {
114  QFileInfo fi( it.next() );
115 
116  if ( !fileRegexp.isEmpty() )
117  {
118  if ( fileRegexp.indexIn( fi.fileName() ) == -1 )
119  {
120  QgsDebugMsg( "provider " + fi.fileName() + " skipped because doesn't match pattern " + filePattern );
121  continue;
122  }
123  }
124 
125  QLibrary myLib( fi.filePath() );
126  if ( !myLib.load() )
127  {
128  QgsDebugMsg( QString( "Checking %1: ...invalid (lib not loadable): %2" ).arg( myLib.fileName(), myLib.errorString() ) );
129  continue;
130  }
131 
132  //MH: Added a further test to detect non-provider plugins linked to provider plugins.
133  //Only pure provider plugins have 'type' not defined
134  isprovider_t *hasType = reinterpret_cast< isprovider_t * >( cast_to_fptr( myLib.resolve( "type" ) ) );
135  if ( hasType )
136  {
137  QgsDebugMsg( QString( "Checking %1: ...invalid (has type method)" ).arg( myLib.fileName() ) );
138  continue;
139  }
140 
141  // get the description and the key for the provider plugin
142  isprovider_t *isProvider = reinterpret_cast< isprovider_t * >( cast_to_fptr( myLib.resolve( "isProvider" ) ) );
143  if ( !isProvider )
144  {
145  QgsDebugMsg( QString( "Checking %1: ...invalid (no isProvider method)" ).arg( myLib.fileName() ) );
146  continue;
147  }
148 
149  // check to see if this is a provider plugin
150  if ( !isProvider() )
151  {
152  QgsDebugMsg( QString( "Checking %1: ...invalid (not a provider)" ).arg( myLib.fileName() ) );
153  continue;
154  }
155 
156  // looks like a provider. get the key and description
157  description_t *pDesc = reinterpret_cast< description_t * >( cast_to_fptr( myLib.resolve( "description" ) ) );
158  if ( !pDesc )
159  {
160  QgsDebugMsg( QString( "Checking %1: ...invalid (no description method)" ).arg( myLib.fileName() ) );
161  continue;
162  }
163 
164  providerkey_t *pKey = reinterpret_cast< providerkey_t * >( cast_to_fptr( myLib.resolve( "providerKey" ) ) );
165  if ( !pKey )
166  {
167  QgsDebugMsg( QString( "Checking %1: ...invalid (no providerKey method)" ).arg( myLib.fileName() ) );
168  continue;
169  }
170 
171  // add this provider to the provider map
172  mProviders[pKey()] = new QgsProviderMetadata( pKey(), pDesc(), myLib.fileName() );
173 
174  // load database drivers
175  databaseDrivers_t *pDatabaseDrivers = reinterpret_cast< databaseDrivers_t * >( cast_to_fptr( myLib.resolve( "databaseDrivers" ) ) );
176  if ( pDatabaseDrivers )
177  {
178  mDatabaseDrivers = pDatabaseDrivers();
179  }
180 
181  // load directory drivers
182  directoryDrivers_t *pDirectoryDrivers = reinterpret_cast< directoryDrivers_t * >( cast_to_fptr( myLib.resolve( "directoryDrivers" ) ) );
183  if ( pDirectoryDrivers )
184  {
185  mDirectoryDrivers = pDirectoryDrivers();
186  }
187 
188  // load protocol drivers
189  protocolDrivers_t *pProtocolDrivers = reinterpret_cast< protocolDrivers_t * >( cast_to_fptr( myLib.resolve( "protocolDrivers" ) ) );
190  if ( pProtocolDrivers )
191  {
192  mProtocolDrivers = pProtocolDrivers();
193  }
194 
195  // now get vector file filters, if any
196  fileVectorFilters_t *pFileVectorFilters = reinterpret_cast< fileVectorFilters_t * >( cast_to_fptr( myLib.resolve( "fileVectorFilters" ) ) );
197  if ( pFileVectorFilters )
198  {
199  QString fileVectorFilters = pFileVectorFilters();
200 
201  if ( !fileVectorFilters.isEmpty() )
202  mVectorFileFilters += fileVectorFilters;
203 
204  QgsDebugMsg( QString( "Checking %1: ...loaded ok (%2 file filters)" ).arg( myLib.fileName() ).arg( fileVectorFilters.split( ";;" ).count() ) );
205  }
206 
207  // now get raster file filters, if any
208  // this replaces deprecated QgsRasterLayer::buildSupportedRasterFileFilter
210  reinterpret_cast< buildsupportedrasterfilefilter_t * >( cast_to_fptr( myLib.resolve( "buildSupportedRasterFileFilter" ) ) );
211  if ( pBuild )
212  {
214  pBuild( fileRasterFilters );
215 
216  QgsDebugMsg( "raster filters: " + fileRasterFilters );
217  if ( !fileRasterFilters.isEmpty() )
218  mRasterFileFilters += fileRasterFilters;
219 
220  QgsDebugMsg( QString( "Checking %1: ...loaded ok (%2 file filters)" ).arg( myLib.fileName() ).arg( fileRasterFilters.split( ";;" ).count() ) );
221  }
222  }
223 } // QgsProviderRegistry ctor
224 
225 
226 // typedef for the unload dataprovider function
228 
229 void QgsProviderRegistry::clean()
230 {
232 
233  Providers::const_iterator it = mProviders.begin();
234 
235  while ( it != mProviders.end() )
236  {
237  QgsDebugMsg( QString( "cleanup:%1" ).arg( it->first ) );
238  QString lib = it->second->library();
239  QLibrary myLib( lib );
240  if ( myLib.isLoaded() )
241  {
242  cleanupProviderFunction_t* cleanupFunc = reinterpret_cast< cleanupProviderFunction_t* >( cast_to_fptr( myLib.resolve( "cleanupProvider" ) ) );
243  if ( cleanupFunc )
244  cleanupFunc();
245  }
246  delete it->second;
247  ++it;
248  }
249 }
250 
252 {
253  clean();
254 }
255 
256 
264 static
266  QString const & providerKey )
267 {
268  QgsProviderRegistry::Providers::const_iterator i =
269  metaData.find( providerKey );
270 
271  if ( i != metaData.end() )
272  {
273  return i->second;
274  }
275 
276  return nullptr;
277 } // findMetadata_
278 
279 
280 
281 QString QgsProviderRegistry::library( QString const & providerKey ) const
282 {
283  QgsProviderMetadata * md = findMetadata_( mProviders, providerKey );
284 
285  if ( md )
286  {
287  return md->library();
288  }
289 
290  return QString();
291 }
292 
293 
295 {
296  Providers::const_iterator it = mProviders.begin();
297 
298  if ( mProviders.empty() )
299  return QObject::tr( "No data provider plugins are available. No vector layers can be loaded" );
300 
301  QString list;
302 
303  if ( asHTML )
304  list += "<ol>";
305 
306  while ( it != mProviders.end() )
307  {
308  if ( asHTML )
309  list += "<li>";
310 
311  list += it->second->description();
312 
313  if ( asHTML )
314  list + "<br></li>";
315  else
316  list += '\n';
317 
318  ++it;
319  }
320 
321  if ( asHTML )
322  list += "</ol>";
323 
324  return list;
325 }
326 
328 {
329  mLibraryDirectory = path;
330  clean();
331  init();
332 }
333 
335 {
336  return mLibraryDirectory;
337 }
338 
339 
340 
341 // typedef for the QgsDataProvider class factory
343 
344 
352 QgsDataProvider *QgsProviderRegistry::provider( QString const & providerKey, QString const & dataSource )
353 {
354  // XXX should I check for and possibly delete any pre-existing providers?
355  // XXX How often will that scenario occur?
356 
357  // load the plugin
358  QString lib = library( providerKey );
359 
360 #ifdef TESTPROVIDERLIB
361  const char *cLib = lib.toUtf8();
362 
363  // test code to help debug provider loading problems
364  // void *handle = dlopen(cLib, RTLD_LAZY);
365  void *handle = dlopen( cOgrLib, RTLD_LAZY | RTLD_GLOBAL );
366  if ( !handle )
367  {
368  QgsLogger::warning( "Error in dlopen" );
369  }
370  else
371  {
372  QgsDebugMsg( "dlopen suceeded" );
373  dlclose( handle );
374  }
375 
376 #endif
377  // load the data provider
378  QLibrary myLib( lib );
379 
380  QgsDebugMsg( "Library name is " + myLib.fileName() );
381  if ( !myLib.load() )
382  {
383  QgsMessageLog::logMessage( QObject::tr( "Failed to load %1: %2" ).arg( lib, myLib.errorString() ) );
384  return nullptr;
385  }
386 
387  classFactoryFunction_t *classFactory = reinterpret_cast< classFactoryFunction_t * >( cast_to_fptr( myLib.resolve( "classFactory" ) ) );
388  if ( !classFactory )
389  {
390  QgsDebugMsg( QString( "Failed to load %1: no classFactory method" ).arg( lib ) );
391  return nullptr;
392  }
393 
394  QgsDataProvider *dataProvider = classFactory( &dataSource );
395  if ( !dataProvider )
396  {
397  QgsMessageLog::logMessage( QObject::tr( "Unable to instantiate the data provider plugin %1" ).arg( lib ) );
398  myLib.unload();
399  return nullptr;
400  }
401 
402  QgsDebugMsg( QString( "Instantiated the data provider plugin: %1" ).arg( dataProvider->name() ) );
403  return dataProvider;
404 } // QgsProviderRegistry::setDataProvider
405 
406 int QgsProviderRegistry::providerCapabilities( const QString &providerKey ) const
407 {
408  QLibrary *library = providerLibrary( providerKey );
409  if ( !library )
410  {
412  }
413 
414  dataCapabilities_t * dataCapabilities = reinterpret_cast< dataCapabilities_t *>( cast_to_fptr( library->resolve( "dataCapabilities" ) ) );
415  if ( !dataCapabilities )
416  {
418  }
419 
420  return dataCapabilities();
421 }
422 
423 // This should be QWidget, not QDialog
425 
427  QWidget * parent, const Qt::WindowFlags& fl )
428 {
429  selectFactoryFunction_t * selectFactory =
430  reinterpret_cast< selectFactoryFunction_t * >( cast_to_fptr( function( providerKey, "selectWidget" ) ) );
431 
432  if ( !selectFactory )
433  return nullptr;
434 
435  return selectFactory( parent, fl );
436 }
437 
438 #if QT_VERSION >= 0x050000
439 QFunctionPointer QgsProviderRegistry::function( QString const & providerKey,
440  QString const & functionName )
441 {
442  QLibrary myLib( library( providerKey ) );
443 
444  QgsDebugMsg( "Library name is " + myLib.fileName() );
445 
446  if ( myLib.load() )
447  {
448  return myLib.resolve( functionName.toAscii().data() );
449  }
450  else
451  {
452  QgsDebugMsg( "Cannot load library: " + myLib.errorString() );
453  return 0;
454  }
455 }
456 #else
457 void *QgsProviderRegistry::function( QString const & providerKey,
458  QString const & functionName )
459 {
460  QLibrary myLib( library( providerKey ) );
461 
462  QgsDebugMsg( "Library name is " + myLib.fileName() );
463 
464  if ( myLib.load() )
465  {
466  return myLib.resolve( functionName.toAscii().data() );
467  }
468  else
469  {
470  QgsDebugMsg( "Cannot load library: " + myLib.errorString() );
471  return nullptr;
472  }
473 }
474 #endif
475 
477 {
478  QLibrary *myLib = new QLibrary( library( providerKey ) );
479 
480  QgsDebugMsg( "Library name is " + myLib->fileName() );
481 
482  if ( myLib->load() )
483  return myLib;
484 
485  QgsDebugMsg( "Cannot load library: " + myLib->errorString() );
486 
487  delete myLib;
488 
489  return nullptr;
490 }
491 
493 {
494  typedef void registerGui_function( QWidget * parent );
495 
496  Q_FOREACH ( const QString &provider, providerList() )
497  {
498  registerGui_function *registerGui = reinterpret_cast< registerGui_function * >( cast_to_fptr( function( provider, "registerGui" ) ) );
499 
500  if ( !registerGui )
501  continue;
502 
503  registerGui( parent );
504  }
505 }
506 
508 {
509  return mVectorFileFilters;
510 }
511 
513 {
514  return mRasterFileFilters;
515 }
516 
518 {
519  return mDatabaseDrivers;
520 }
521 
523 {
524  return mDirectoryDrivers;
525 }
526 
528 {
529  return mProtocolDrivers;
530 }
531 
533 {
534  QStringList lst;
535  for ( Providers::const_iterator it = mProviders.begin(); it != mProviders.end(); ++it )
536  {
537  lst.append( it->first );
538  }
539  return lst;
540 }
541 
543 {
544  return findMetadata_( mProviders, providerKey );
545 }
static QgsProviderRegistry * instance(const QString &pluginPath=QString::null)
Means of accessing canonical single instance.
QgsDataProvider * classFactoryFunction_t(const QString *)
QString databaseDrivers_t()
virtual QString databaseDrivers() const
Return a string containing the available database drivers.
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
virtual void setTitle(const QString &title)=0
set title for the messages
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QString library(const QString &providerKey) const
Return path for the library of the provider.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:124
const QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Return metadata of the provider or NULL if not found.
const QDir & libraryDirectory() const
Return library directory where plugins are found.
void registerGuis(QWidget *widget)
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()
Remove 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
QStringList providerList() const
Return list of available providers by their keys.
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 library() const
This returns the library file name.
QString path() const
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
QFileInfoList entryInfoList(QFlags< QDir::Filter > filters, QFlags< QDir::SortFlag > sort) const
bool isEmpty() const
virtual QString protocolDrivers() const
Return a string containing the available protocol drivers.
int providerCapabilities(const QString &providerKey) const
Return the provider capabilities.
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.
virtual QString directoryDrivers() const
Return a string containing the available directory drivers.
void buildsupportedrasterfilefilter_t(QString &theFileFiltersString)
virtual QString fileRasterFilters() const
Return raster file filter string.
bool load()
QString fileVectorFilters_t()
QString pluginList(bool asHtml=false) const
Return list of provider plugins found.
std::map< QString, QgsProviderMetadata * > Providers
Open the given vector data source.
A registry / canonical manager of data providers.
QLibrary * providerLibrary(const QString &providerKey) const
virtual void showMessage(bool blocking=true)=0
display the message to the user and deletes itself
virtual QString fileVectorFilters() const
Return vector file filter string.
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)
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:258
QString protocolDrivers_t()
Interface for showing messages from QGIS in GUI independent way.
int dataCapabilities_t()
QWidget * selectWidget(const QString &providerKey, QWidget *parent=nullptr, const Qt::WindowFlags &fl=nullptr)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QByteArray toAscii() const
QString errorString() const
QByteArray toUtf8() const