QGIS API Documentation  master-6227475
src/core/qgsproviderregistry.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                     qgsproviderregistry.cpp  -  Singleton class for
00003                     registering data providers.
00004                              -------------------
00005     begin                : Sat Jan 10 2004
00006     copyright            : (C) 2004 by Gary E.Sherman
00007     email                : sherman at mrcc.com
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 
00019 #include "qgsproviderregistry.h"
00020 
00021 #include <QString>
00022 #include <QDir>
00023 #include <QLibrary>
00024 
00025 
00026 #include "qgis.h"
00027 #include "qgsdataprovider.h"
00028 #include "qgslogger.h"
00029 #include "qgsmessageoutput.h"
00030 #include "qgsmessagelog.h"
00031 #include "qgsprovidermetadata.h"
00032 #include "qgsvectorlayer.h"
00033 
00034 
00035 // typedefs for provider plugin functions of interest
00036 typedef QString providerkey_t();
00037 typedef QString description_t();
00038 typedef bool    isprovider_t();
00039 typedef QString fileVectorFilters_t();
00040 typedef QString databaseDrivers_t();
00041 typedef QString directoryDrivers_t();
00042 typedef QString protocolDrivers_t();
00043 //typedef int dataCapabilities_t();
00044 //typedef QgsDataItem * dataItem_t(QString);
00045 
00046 QgsProviderRegistry *QgsProviderRegistry::_instance = 0;
00047 
00048 QgsProviderRegistry *QgsProviderRegistry::instance( QString pluginPath )
00049 {
00050   if ( _instance == 0 )
00051   {
00052     _instance = new QgsProviderRegistry( pluginPath );
00053   }
00054 
00055   return _instance;
00056 
00057 } // QgsProviderRegistry::instance
00058 
00059 
00060 
00061 QgsProviderRegistry::QgsProviderRegistry( QString pluginPath )
00062 {
00063   // At startup, examine the libs in the qgis/lib dir and store those that
00064   // are a provider shared lib
00065   // check all libs in the current plugin directory and get name and descriptions
00066   //TODO figure out how to register and identify data source plugin for a specific
00067   //TODO layer type
00068   /* char **argv = qApp->argv();
00069      QString appDir = argv[0];
00070      int bin = appDir.findRev("/bin", -1, false);
00071      QString baseDir = appDir.left(bin);
00072      QString mLibraryDirectory = baseDir + "/lib"; */
00073   mLibraryDirectory = pluginPath;
00074 
00075   mLibraryDirectory.setSorting( QDir::Name | QDir::IgnoreCase );
00076   mLibraryDirectory.setFilter( QDir::Files | QDir::NoSymLinks );
00077 
00078 #if defined(WIN32) || defined(__CYGWIN__)
00079   mLibraryDirectory.setNameFilters( QStringList( "*.dll" ) );
00080 #elif ANDROID
00081   mLibraryDirectory.setNameFilters( QStringList( "*provider.so" ) );
00082 #else
00083   mLibraryDirectory.setNameFilters( QStringList( "*.so" ) );
00084 #endif
00085 
00086   QgsDebugMsg( QString( "Checking %1 for provider plugins" ).arg( mLibraryDirectory.path() ) );
00087 
00088   if ( mLibraryDirectory.count() == 0 )
00089   {
00090     QString msg = QObject::tr( "No QGIS data provider plugins found in:\n%1\n" ).arg( mLibraryDirectory.path() );
00091     msg += QObject::tr( "No vector layers can be loaded. Check your QGIS installation" );
00092 
00093     QgsMessageOutput* output = QgsMessageOutput::createMessageOutput();
00094     output->setTitle( QObject::tr( "No Data Providers" ) );
00095     output->setMessage( msg, QgsMessageOutput::MessageText );
00096     output->showMessage();
00097   }
00098   else
00099   {
00100     const QFileInfoList list = mLibraryDirectory.entryInfoList();
00101     QListIterator<QFileInfo> it( list );
00102     QFileInfo fi;
00103 
00104     while ( it.hasNext() )
00105     {
00106       fi = it.next();
00107 
00108       QLibrary *myLib = new QLibrary( fi.filePath() );
00109 
00110       bool loaded = myLib->load();
00111       //we will build up a debug message and print on one line to avoid terminal spam
00112       QString myMessage =  "Checking  " + myLib->fileName() + " : " ;
00113 
00114       if ( loaded )
00115       {
00116         // get the description and the key for the provider plugin
00117         isprovider_t *isProvider = ( isprovider_t * ) cast_to_fptr( myLib->resolve( "isProvider" ) );
00118 
00119         //MH: Added a further test to detect non-provider plugins linked to provider plugins.
00120         //Only pure provider plugins have 'type' not defined
00121         isprovider_t *hasType = ( isprovider_t * ) cast_to_fptr( myLib->resolve( "type" ) );
00122 
00123         if ( !hasType && isProvider )
00124         {
00125           // check to see if this is a provider plugin
00126           if ( isProvider() )
00127           {
00128             // looks like a provider. get the key and description
00129             description_t *pDesc = ( description_t * ) cast_to_fptr( myLib->resolve( "description" ) );
00130             providerkey_t *pKey = ( providerkey_t * ) cast_to_fptr( myLib->resolve( "providerKey" ) );
00131             if ( pDesc && pKey )
00132             {
00133               // add this provider to the provider map
00134               mProviders[pKey()] =
00135                 new QgsProviderMetadata( pKey(), pDesc(), myLib->fileName() );
00136               //myMessage += "Loaded " + QString(pDesc()) + " ok";
00137 
00138               // now get vector file filters, if any
00139               fileVectorFilters_t *pFileVectorFilters =
00140                 ( fileVectorFilters_t * ) cast_to_fptr( myLib->resolve( "fileVectorFilters" ) );
00141               //load database drivers
00142               databaseDrivers_t *pDatabaseDrivers =
00143                 ( databaseDrivers_t * ) cast_to_fptr( myLib->resolve( "databaseDrivers" ) );
00144               if ( pDatabaseDrivers )
00145               {
00146                 mDatabaseDrivers = pDatabaseDrivers();
00147               }
00148               //load directory drivers
00149               directoryDrivers_t *pDirectoryDrivers =
00150                 ( directoryDrivers_t * ) cast_to_fptr( myLib->resolve( "directoryDrivers" ) );
00151               if ( pDirectoryDrivers )
00152               {
00153                 mDirectoryDrivers = pDirectoryDrivers();
00154               }
00155               //load protocol drivers
00156               protocolDrivers_t *pProtocolDrivers =
00157                 ( protocolDrivers_t * ) cast_to_fptr( myLib->resolve( "protocolDrivers" ) );
00158               if ( pProtocolDrivers )
00159               {
00160                 mProtocolDrivers = pProtocolDrivers();
00161               }
00162 
00163               if ( pFileVectorFilters )
00164               {
00165                 QString vectorFileFilters = pFileVectorFilters();
00166 
00167                 // now get vector file filters, if any
00168                 fileVectorFilters_t *pVectorFileFilters =
00169                   ( fileVectorFilters_t * ) cast_to_fptr( myLib->resolve( "fileVectorFilters" ) );
00170 
00171                 if ( pVectorFileFilters )
00172                 {
00173                   QString fileVectorFilters = pVectorFileFilters();
00174 
00175                   if ( ! fileVectorFilters.isEmpty() )
00176                   {
00177                     mVectorFileFilters += fileVectorFilters;
00178                     myMessage += QString( "... loaded ok (and with %1 file filters)" ).
00179                                  arg( fileVectorFilters.split( ";;" ).count() );
00180                   }
00181                   else
00182                   {
00183                     //myMessage += ", but it has no vector file filters for " + QString(pKey());
00184                     myMessage += "... loaded ok (0 file filters)";
00185                   }
00186                 }
00187               }
00188               else
00189               {
00190                 //myMessage += ", but unable to invoke fileVectorFilters()";
00191                 myMessage += "... loaded ok (null file filters)";
00192               }
00193             }
00194             else
00195             {
00196               //myMessage += ", but unable to find one of the required provider functions (providerKey() or description()) in ";
00197               myMessage += "...not usable";
00198 
00199             }
00200           }
00201           else
00202           {
00203             //myMessage += ", but this is not a valid provider, skipping.";
00204             myMessage += "..invalid";
00205           }
00206         }
00207         else
00208         {
00209           //myMessage += ", but this is not a valid provider or has no type, skipping.";
00210           myMessage += "..invalid (no type)";
00211         }
00212       }
00213       else
00214       {
00215         myMessage += "...invalid (lib not loadable): ";
00216         myMessage += myLib->errorString();
00217       }
00218 
00219       QgsDebugMsg( myMessage );
00220 
00221       delete myLib;
00222     }
00223   }
00224 
00225 } // QgsProviderRegistry ctor
00226 
00227 
00228 QgsProviderRegistry::~QgsProviderRegistry()
00229 {
00230 }
00231 
00232 
00240 static
00241 QgsProviderMetadata * findMetadata_( QgsProviderRegistry::Providers const & metaData,
00242                                      QString const & providerKey )
00243 {
00244   QgsProviderRegistry::Providers::const_iterator i =
00245     metaData.find( providerKey );
00246 
00247   if ( i != metaData.end() )
00248   {
00249     return i->second;
00250   }
00251 
00252   return 0x0;
00253 } // findMetadata_
00254 
00255 
00256 
00257 QString QgsProviderRegistry::library( QString const & providerKey ) const
00258 {
00259   QgsProviderMetadata * md = findMetadata_( mProviders, providerKey );
00260 
00261   if ( md )
00262   {
00263     return md->library();
00264   }
00265 
00266   return QString();
00267 }
00268 
00269 
00270 QString QgsProviderRegistry::pluginList( bool asHTML ) const
00271 {
00272   Providers::const_iterator it = mProviders.begin();
00273   QString list;
00274 
00275   if ( mProviders.empty() )
00276   {
00277     list = QObject::tr( "No data provider plugins are available. No vector layers can be loaded" );
00278   }
00279   else
00280   {
00281     if ( asHTML )
00282     {
00283       list += "<ol>";
00284     }
00285     while ( it != mProviders.end() )
00286     {
00287       QgsProviderMetadata *mp = ( *it ).second;
00288 
00289       if ( asHTML )
00290       {
00291         list += "<li>" + mp->description() + "<br>";
00292       }
00293       else
00294       {
00295         list += mp->description() + "\n";
00296       }
00297 
00298       it++;
00299     }
00300     if ( asHTML )
00301     {
00302       list += "</ol>";
00303     }
00304   }
00305 
00306   return list;
00307 }
00308 
00309 
00310 void QgsProviderRegistry::setLibraryDirectory( QDir const & path )
00311 {
00312   mLibraryDirectory = path;
00313 }
00314 
00315 
00316 QDir const & QgsProviderRegistry::libraryDirectory() const
00317 {
00318   return mLibraryDirectory;
00319 }
00320 
00321 
00322 
00323 // typedef for the QgsDataProvider class factory
00324 typedef QgsDataProvider * classFactoryFunction_t( const QString * );
00325 
00326 
00327 
00335 QgsDataProvider *QgsProviderRegistry::provider( QString const & providerKey, QString const & dataSource )
00336 {
00337   // XXX should I check for and possibly delete any pre-existing providers?
00338   // XXX How often will that scenario occur?
00339 
00340   // load the plugin
00341   QString lib = library( providerKey );
00342 
00343 #ifdef TESTPROVIDERLIB
00344   const char *cLib = lib.toUtf8();
00345 
00346   // test code to help debug provider loading problems
00347   //  void *handle = dlopen(cLib, RTLD_LAZY);
00348   void *handle = dlopen( cOgrLib, RTLD_LAZY | RTLD_GLOBAL );
00349   if ( !handle )
00350   {
00351     QgsLogger::warning( "Error in dlopen" );
00352   }
00353   else
00354   {
00355     QgsDebugMsg( "dlopen suceeded" );
00356     dlclose( handle );
00357   }
00358 
00359 #endif
00360 
00361   // load the data provider
00362   QLibrary* myLib = new QLibrary( lib );
00363 
00364   QgsDebugMsg( "Library name is " + myLib->fileName() );
00365 
00366   bool loaded = myLib->load();
00367 
00368   if ( loaded )
00369   {
00370     QgsDebugMsg( "Loaded data provider library" );
00371     QgsDebugMsg( "Attempting to resolve the classFactory function" );
00372 
00373     classFactoryFunction_t * classFactory =
00374       ( classFactoryFunction_t * ) cast_to_fptr( myLib->resolve( "classFactory" ) );
00375 
00376     if ( classFactory )
00377     {
00378       QgsDebugMsg( "Getting pointer to a dataProvider object from the library" );
00379 
00380       //XXX - This was a dynamic cast but that kills the Windows
00381       //      version big-time with an abnormal termination error
00382       //      QgsDataProvider* dataProvider = (QgsDataProvider*)
00383       //      (classFactory((const char*)(dataSource.utf8())));
00384 
00385       QgsDataProvider * dataProvider = ( *classFactory )( &dataSource );
00386 
00387       if ( dataProvider )
00388       {
00389         QgsDebugMsg( "Instantiated the data provider plugin" );
00390         QgsDebugMsg( "provider name: " + dataProvider->name() );
00391 
00392         delete myLib;
00393         return dataProvider;
00394       }
00395       else
00396       {
00397         QgsMessageLog::logMessage( QObject::tr( "Unable to instantiate the data provider plugin %1" ).arg( lib ) );
00398 
00399         delete dataProvider;
00400 
00401         myLib->unload();
00402         delete myLib;
00403         return 0;
00404       }
00405     }
00406   }
00407   else
00408   {
00409     QgsMessageLog::logMessage( QObject::tr( "Failed to load %1: %2" ).arg( lib ).arg( myLib->errorString() ) );
00410     delete myLib;
00411     return 0;
00412   }
00413 
00414   QgsDebugMsg( "exiting" );
00415 
00416   return 0;  // factory didn't exist
00417 
00418 } // QgsProviderRegistry::setDataProvider
00419 
00420 // This should be QWidget, not QDialog
00421 typedef QWidget * selectFactoryFunction_t( QWidget * parent, Qt::WFlags fl );
00422 
00423 QWidget* QgsProviderRegistry::selectWidget( const QString & providerKey,
00424     QWidget * parent, Qt::WFlags fl )
00425 {
00426   selectFactoryFunction_t * selectFactory =
00427     ( selectFactoryFunction_t * ) cast_to_fptr( function( providerKey, "selectWidget" ) );
00428 
00429   if ( !selectFactory )
00430     return 0;
00431 
00432   return selectFactory( parent, fl );
00433 }
00434 
00435 void * QgsProviderRegistry::function( QString const & providerKey,
00436                                       QString const & functionName )
00437 {
00438   QString lib = library( providerKey );
00439 
00440   QLibrary* myLib = new QLibrary( lib );
00441 
00442   QgsDebugMsg( "Library name is " + myLib->fileName() );
00443 
00444   bool loaded = myLib->load();
00445 
00446   if ( loaded )
00447   {
00448     void * ptr = myLib->resolve( functionName.toAscii().data() );
00449     delete myLib;
00450     return ptr;
00451   }
00452   QgsDebugMsg( "Cannot load library: " + myLib->errorString() );
00453   delete myLib;
00454   return 0;
00455 }
00456 
00457 QLibrary *QgsProviderRegistry::providerLibrary( QString const & providerKey ) const
00458 {
00459   QString lib = library( providerKey );
00460 
00461   QLibrary *myLib = new QLibrary( lib );
00462 
00463   QgsDebugMsg( "Library name is " + myLib->fileName() );
00464 
00465   bool loaded = myLib->load();
00466 
00467   if ( loaded )
00468   {
00469     return myLib;
00470   }
00471   QgsDebugMsg( "Cannot load library: " + myLib->errorString() );
00472   delete myLib;
00473   return 0;
00474 }
00475 
00476 void QgsProviderRegistry::registerGuis( QWidget *parent )
00477 {
00478   typedef void registerGui_function( QWidget * parent );
00479 
00480   foreach ( const QString &provider, providerList() )
00481   {
00482     registerGui_function *registerGui = ( registerGui_function * ) cast_to_fptr( function( provider, "registerGui" ) );
00483 
00484     if ( !registerGui )
00485       continue;
00486 
00487     registerGui( parent );
00488   }
00489 }
00490 
00491 QString QgsProviderRegistry::fileVectorFilters() const
00492 {
00493   return mVectorFileFilters;
00494 }
00495 
00496 QString QgsProviderRegistry::databaseDrivers() const
00497 {
00498   return mDatabaseDrivers;
00499 }
00500 
00501 QString QgsProviderRegistry::directoryDrivers() const
00502 {
00503   return mDirectoryDrivers;
00504 }
00505 
00506 QString QgsProviderRegistry::protocolDrivers() const
00507 {
00508   return mProtocolDrivers;
00509 }
00510 
00511 
00512 QStringList QgsProviderRegistry::providerList() const
00513 {
00514   QStringList lst;
00515   for ( Providers::const_iterator it = mProviders.begin(); it != mProviders.end(); it++ )
00516   {
00517     lst.append( it->first );
00518   }
00519   return lst;
00520 }
00521 
00522 
00523 const QgsProviderMetadata* QgsProviderRegistry::providerMetadata( const QString& providerKey ) const
00524 {
00525   return findMetadata_( mProviders, providerKey );
00526 }
00527 
00528 
00529 #if 0
00530 QgsDataProvider *
00531 QgsProviderRegistry::openVector( QString const & dataSource, QString const & providerKey )
00532 {
00533   return getProvider( providerKey, dataSource );
00534 } // QgsProviderRegistry::openVector
00535 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines