QGIS API Documentation  2.12.0-Lyon
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  mLibraryDirectory.setSorting( QDir::Name | QDir::IgnoreCase );
73  mLibraryDirectory.setFilter( QDir::Files | QDir::NoSymLinks );
74 
75 #if defined(Q_OS_WIN) || defined(__CYGWIN__)
76  mLibraryDirectory.setNameFilters( QStringList( "*.dll" ) );
77 #elif ANDROID
78  mLibraryDirectory.setNameFilters( QStringList( "*provider.so" ) );
79 #else
80  mLibraryDirectory.setNameFilters( QStringList( "*.so" ) );
81 #endif
82 
83  QgsDebugMsg( QString( "Checking %1 for provider plugins" ).arg( mLibraryDirectory.path() ) );
84 
85  if ( mLibraryDirectory.count() == 0 )
86  {
87  QString msg = QObject::tr( "No QGIS data provider plugins found in:\n%1\n" ).arg( mLibraryDirectory.path() );
88  msg += QObject::tr( "No vector layers can be loaded. Check your QGIS installation" );
89 
91  output->setTitle( QObject::tr( "No Data Providers" ) );
93  output->showMessage();
94  return;
95  }
96 
97  // provider file regex pattern, only files matching the pattern are loaded if the variable is defined
98  QString filePattern = getenv( "QGIS_PROVIDER_FILE" );
99  QRegExp fileRegexp;
100  if ( !filePattern.isEmpty() )
101  {
102  fileRegexp.setPattern( filePattern );
103  }
104 
105  QListIterator<QFileInfo> it( mLibraryDirectory.entryInfoList() );
106  while ( it.hasNext() )
107  {
108  QFileInfo fi( it.next() );
109 
110  if ( !fileRegexp.isEmpty() )
111  {
112  if ( fileRegexp.indexIn( fi.fileName() ) == -1 )
113  {
114  QgsDebugMsg( "provider " + fi.fileName() + " skipped because doesn't match pattern " + filePattern );
115  continue;
116  }
117  }
118 
119  QLibrary myLib( fi.filePath() );
120  if ( !myLib.load() )
121  {
122  QgsDebugMsg( QString( "Checking %1: ...invalid (lib not loadable): %2" ).arg( myLib.fileName(), myLib.errorString() ) );
123  continue;
124  }
125 
126  //MH: Added a further test to detect non-provider plugins linked to provider plugins.
127  //Only pure provider plugins have 'type' not defined
128  isprovider_t *hasType = ( isprovider_t * ) cast_to_fptr( myLib.resolve( "type" ) );
129  if ( hasType )
130  {
131  QgsDebugMsg( QString( "Checking %1: ...invalid (has type method)" ).arg( myLib.fileName() ) );
132  continue;
133  }
134 
135  // get the description and the key for the provider plugin
136  isprovider_t *isProvider = ( isprovider_t * ) cast_to_fptr( myLib.resolve( "isProvider" ) );
137  if ( !isProvider )
138  {
139  QgsDebugMsg( QString( "Checking %1: ...invalid (no isProvider method)" ).arg( myLib.fileName() ) );
140  continue;
141  }
142 
143  // check to see if this is a provider plugin
144  if ( !isProvider() )
145  {
146  QgsDebugMsg( QString( "Checking %1: ...invalid (not a provider)" ).arg( myLib.fileName() ) );
147  continue;
148  }
149 
150  // looks like a provider. get the key and description
151  description_t *pDesc = ( description_t * ) cast_to_fptr( myLib.resolve( "description" ) );
152  if ( !pDesc )
153  {
154  QgsDebugMsg( QString( "Checking %1: ...invalid (no description method)" ).arg( myLib.fileName() ) );
155  continue;
156  }
157 
158  providerkey_t *pKey = ( providerkey_t * ) cast_to_fptr( myLib.resolve( "providerKey" ) );
159  if ( !pKey )
160  {
161  QgsDebugMsg( QString( "Checking %1: ...invalid (no providerKey method)" ).arg( myLib.fileName() ) );
162  continue;
163  }
164 
165  // add this provider to the provider map
166  mProviders[pKey()] = new QgsProviderMetadata( pKey(), pDesc(), myLib.fileName() );
167 
168  // load database drivers
169  databaseDrivers_t *pDatabaseDrivers = ( databaseDrivers_t * ) cast_to_fptr( myLib.resolve( "databaseDrivers" ) );
170  if ( pDatabaseDrivers )
171  {
172  mDatabaseDrivers = pDatabaseDrivers();
173  }
174 
175  // load directory drivers
176  directoryDrivers_t *pDirectoryDrivers = ( directoryDrivers_t * ) cast_to_fptr( myLib.resolve( "directoryDrivers" ) );
177  if ( pDirectoryDrivers )
178  {
179  mDirectoryDrivers = pDirectoryDrivers();
180  }
181 
182  // load protocol drivers
183  protocolDrivers_t *pProtocolDrivers = ( protocolDrivers_t * ) cast_to_fptr( myLib.resolve( "protocolDrivers" ) );
184  if ( pProtocolDrivers )
185  {
186  mProtocolDrivers = pProtocolDrivers();
187  }
188 
189  // now get vector file filters, if any
190  fileVectorFilters_t *pFileVectorFilters = ( fileVectorFilters_t * ) cast_to_fptr( myLib.resolve( "fileVectorFilters" ) );
191  if ( pFileVectorFilters )
192  {
193  QString fileVectorFilters = pFileVectorFilters();
194 
195  if ( !fileVectorFilters.isEmpty() )
196  mVectorFileFilters += fileVectorFilters;
197 
198  QgsDebugMsg( QString( "Checking %1: ...loaded ok (%2 file filters)" ).arg( myLib.fileName() ).arg( fileVectorFilters.split( ";;" ).count() ) );
199  }
200 
201  // now get raster file filters, if any
202  // this replaces deprecated QgsRasterLayer::buildSupportedRasterFileFilter
204  ( buildsupportedrasterfilefilter_t * ) cast_to_fptr( myLib.resolve( "buildSupportedRasterFileFilter" ) );
205  if ( pBuild )
206  {
208  pBuild( fileRasterFilters );
209 
210  QgsDebugMsg( "raster filters: " + fileRasterFilters );
211  if ( !fileRasterFilters.isEmpty() )
212  mRasterFileFilters += fileRasterFilters;
213 
214  QgsDebugMsg( QString( "Checking %1: ...loaded ok (%2 file filters)" ).arg( myLib.fileName() ).arg( fileRasterFilters.split( ";;" ).count() ) );
215  }
216  }
217 } // QgsProviderRegistry ctor
218 
219 
220 // typedef for the unload dataprovider function
222 
224 {
226 
227  Providers::const_iterator it = mProviders.begin();
228 
229  while ( it != mProviders.end() )
230  {
231  QgsDebugMsg( QString( "cleanup:%1" ).arg( it->first ) );
232  QString lib = it->second->library();
233  QLibrary myLib( lib );
234  if ( myLib.isLoaded() )
235  {
236  cleanupProviderFunction_t* cleanupFunc = ( cleanupProviderFunction_t* ) cast_to_fptr( myLib.resolve( "cleanupProvider" ) );
237  if ( cleanupFunc )
238  cleanupFunc();
239  }
240  delete it->second;
241  ++it;
242  }
243 }
244 
245 
253 static
255  QString const & providerKey )
256 {
257  QgsProviderRegistry::Providers::const_iterator i =
258  metaData.find( providerKey );
259 
260  if ( i != metaData.end() )
261  {
262  return i->second;
263  }
264 
265  return 0x0;
266 } // findMetadata_
267 
268 
269 
270 QString QgsProviderRegistry::library( QString const & providerKey ) const
271 {
272  QgsProviderMetadata * md = findMetadata_( mProviders, providerKey );
273 
274  if ( md )
275  {
276  return md->library();
277  }
278 
279  return QString();
280 }
281 
282 
284 {
285  Providers::const_iterator it = mProviders.begin();
286 
287  if ( mProviders.empty() )
288  return QObject::tr( "No data provider plugins are available. No vector layers can be loaded" );
289 
290  QString list;
291 
292  if ( asHTML )
293  list += "<ol>";
294 
295  while ( it != mProviders.end() )
296  {
297  if ( asHTML )
298  list += "<li>";
299 
300  list += it->second->description();
301 
302  if ( asHTML )
303  list + "<br></li>";
304  else
305  list += "\n";
306 
307  ++it;
308  }
309 
310  if ( asHTML )
311  list += "</ol>";
312 
313  return list;
314 }
315 
316 
318 {
319  mLibraryDirectory = path;
320 }
321 
322 
324 {
325  return mLibraryDirectory;
326 }
327 
328 
329 
330 // typedef for the QgsDataProvider class factory
332 
333 
341 QgsDataProvider *QgsProviderRegistry::provider( QString const & providerKey, QString const & dataSource )
342 {
343  // XXX should I check for and possibly delete any pre-existing providers?
344  // XXX How often will that scenario occur?
345 
346  // load the plugin
347  QString lib = library( providerKey );
348 
349 #ifdef TESTPROVIDERLIB
350  const char *cLib = lib.toUtf8();
351 
352  // test code to help debug provider loading problems
353  // void *handle = dlopen(cLib, RTLD_LAZY);
354  void *handle = dlopen( cOgrLib, RTLD_LAZY | RTLD_GLOBAL );
355  if ( !handle )
356  {
357  QgsLogger::warning( "Error in dlopen" );
358  }
359  else
360  {
361  QgsDebugMsg( "dlopen suceeded" );
362  dlclose( handle );
363  }
364 
365 #endif
366  // load the data provider
367  QLibrary myLib( lib );
368 
369  QgsDebugMsg( "Library name is " + myLib.fileName() );
370  if ( !myLib.load() )
371  {
372  QgsMessageLog::logMessage( QObject::tr( "Failed to load %1: %2" ).arg( lib, myLib.errorString() ) );
373  return 0;
374  }
375 
376  classFactoryFunction_t *classFactory = ( classFactoryFunction_t * ) cast_to_fptr( myLib.resolve( "classFactory" ) );
377  if ( !classFactory )
378  {
379  QgsDebugMsg( QString( "Failed to load %1: no classFactory method" ).arg( lib ) );
380  return 0;
381  }
382 
383  QgsDataProvider *dataProvider = classFactory( &dataSource );
384  if ( !dataProvider )
385  {
386  QgsMessageLog::logMessage( QObject::tr( "Unable to instantiate the data provider plugin %1" ).arg( lib ) );
387  myLib.unload();
388  return 0;
389  }
390 
391  QgsDebugMsg( QString( "Instantiated the data provider plugin: %1" ).arg( dataProvider->name() ) );
392  return dataProvider;
393 } // QgsProviderRegistry::setDataProvider
394 
395 int QgsProviderRegistry::providerCapabilities( const QString &providerKey ) const
396 {
397  QLibrary *library = providerLibrary( providerKey );
398  if ( !library )
399  {
401  }
402 
403  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
404  if ( !dataCapabilities )
405  {
407  }
408 
409  return dataCapabilities();
410 }
411 
412 // This should be QWidget, not QDialog
414 
416  QWidget * parent, const Qt::WindowFlags& fl )
417 {
418  selectFactoryFunction_t * selectFactory =
419  ( selectFactoryFunction_t * ) cast_to_fptr( function( providerKey, "selectWidget" ) );
420 
421  if ( !selectFactory )
422  return 0;
423 
424  return selectFactory( parent, fl );
425 }
426 
427 #if QT_VERSION >= 0x050000
428 QFunctionPointer QgsProviderRegistry::function( QString const & providerKey,
429  QString const & functionName )
430 {
431  QLibrary myLib( library( providerKey ) );
432 
433  QgsDebugMsg( "Library name is " + myLib.fileName() );
434 
435  if ( myLib.load() )
436  {
437  return myLib.resolve( functionName.toAscii().data() );
438  }
439  else
440  {
441  QgsDebugMsg( "Cannot load library: " + myLib.errorString() );
442  return 0;
443  }
444 }
445 #else
446 void *QgsProviderRegistry::function( QString const & providerKey,
447  QString const & functionName )
448 {
449  QLibrary myLib( library( providerKey ) );
450 
451  QgsDebugMsg( "Library name is " + myLib.fileName() );
452 
453  if ( myLib.load() )
454  {
455  return myLib.resolve( functionName.toAscii().data() );
456  }
457  else
458  {
459  QgsDebugMsg( "Cannot load library: " + myLib.errorString() );
460  return 0;
461  }
462 }
463 #endif
464 
466 {
467  QLibrary *myLib = new QLibrary( library( providerKey ) );
468 
469  QgsDebugMsg( "Library name is " + myLib->fileName() );
470 
471  if ( myLib->load() )
472  return myLib;
473 
474  QgsDebugMsg( "Cannot load library: " + myLib->errorString() );
475 
476  delete myLib;
477 
478  return 0;
479 }
480 
482 {
483  typedef void registerGui_function( QWidget * parent );
484 
485  Q_FOREACH ( const QString &provider, providerList() )
486  {
487  registerGui_function *registerGui = ( registerGui_function * ) cast_to_fptr( function( provider, "registerGui" ) );
488 
489  if ( !registerGui )
490  continue;
491 
492  registerGui( parent );
493  }
494 }
495 
497 {
498  return mVectorFileFilters;
499 }
500 
502 {
503  return mRasterFileFilters;
504 }
505 
507 {
508  return mDatabaseDrivers;
509 }
510 
512 {
513  return mDirectoryDrivers;
514 }
515 
517 {
518  return mProtocolDrivers;
519 }
520 
522 {
523  QStringList lst;
524  for ( Providers::const_iterator it = mProviders.begin(); it != mProviders.end(); ++it )
525  {
526  lst.append( it->first );
527  }
528  return lst;
529 }
530 
532 {
533  return findMetadata_( mProviders, providerKey );
534 }
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".
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'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()
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()
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()
int count(const T &value) const
void append(const T &value)
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
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'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.
bool isLoaded() const
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)
QWidget * selectWidget(const QString &providerKey, QWidget *parent=0, const Qt::WindowFlags &fl=0)
int count() const
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:241
QString protocolDrivers_t()
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
const QString & library() const
This returns the library file name.
QByteArray toAscii() const
QString errorString() const
QByteArray toUtf8() const