Quantum GIS API Documentation  1.8
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 #ifdef WIN32
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         if ( dataProvider->isValid() )
00393         {
00394           delete myLib;
00395           return dataProvider;
00396         }
00397         else
00398         {
00399           // this is likely because the dataSource is invalid, and isn't
00400           // necessarily a reflection on the data provider itself
00401           QgsDebugMsg( "Invalid data provider" );
00402 
00403           delete dataProvider;
00404 
00405           myLib->unload();
00406           delete myLib;
00407           return 0;
00408         }
00409       }
00410       else
00411       {
00412         QgsMessageLog::logMessage( QObject::tr( "Unable to instantiate the data provider plugin %1" ).arg( lib ) );
00413 
00414         delete dataProvider;
00415 
00416         myLib->unload();
00417         delete myLib;
00418         return 0;
00419       }
00420     }
00421   }
00422   else
00423   {
00424     QgsMessageLog::logMessage( QObject::tr( "Failed to load %1: %2" ).arg( lib ).arg( myLib->errorString() ) );
00425     delete myLib;
00426     return 0;
00427   }
00428 
00429   QgsDebugMsg( "exiting" );
00430 
00431   return 0;  // factory didn't exist
00432 
00433 } // QgsProviderRegistry::setDataProvider
00434 
00435 // This should be QWidget, not QDialog
00436 typedef QWidget * selectFactoryFunction_t( QWidget * parent, Qt::WFlags fl );
00437 
00438 QWidget* QgsProviderRegistry::selectWidget( const QString & providerKey,
00439     QWidget * parent, Qt::WFlags fl )
00440 {
00441   QLibrary *myLib = providerLibrary( providerKey );
00442   if ( !myLib )
00443     return 0;
00444 
00445   selectFactoryFunction_t * selectFactory =
00446     ( selectFactoryFunction_t * ) cast_to_fptr( myLib->resolve( "selectWidget" ) );
00447 
00448   if ( !selectFactory )
00449     return 0;
00450 
00451   return selectFactory( parent, fl );
00452 }
00453 
00454 
00455 void * QgsProviderRegistry::function( QString const & providerKey,
00456                                       QString const & functionName )
00457 {
00458   QString lib = library( providerKey );
00459 
00460   QLibrary* myLib = new QLibrary( lib );
00461 
00462   QgsDebugMsg( "Library name is " + myLib->fileName() );
00463 
00464   bool loaded = myLib->load();
00465 
00466   if ( loaded )
00467   {
00468     void * ptr = myLib->resolve( functionName.toAscii().data() );
00469     delete myLib;
00470     return ptr;
00471   }
00472   delete myLib;
00473   return 0;
00474 }
00475 
00476 QLibrary *QgsProviderRegistry::providerLibrary( QString const & providerKey ) const
00477 {
00478   QString lib = library( providerKey );
00479 
00480   QLibrary *myLib = new QLibrary( lib );
00481 
00482   QgsDebugMsg( "Library name is " + myLib->fileName() );
00483 
00484   bool loaded = myLib->load();
00485 
00486   if ( loaded )
00487   {
00488     return myLib;
00489   }
00490   delete myLib;
00491   return 0;
00492 }
00493 
00494 QString QgsProviderRegistry::fileVectorFilters() const
00495 {
00496   return mVectorFileFilters;
00497 }
00498 
00499 QString QgsProviderRegistry::databaseDrivers() const
00500 {
00501   return mDatabaseDrivers;
00502 }
00503 
00504 QString QgsProviderRegistry::directoryDrivers() const
00505 {
00506   return mDirectoryDrivers;
00507 }
00508 
00509 QString QgsProviderRegistry::protocolDrivers() const
00510 {
00511   return mProtocolDrivers;
00512 }
00513 
00514 
00515 QStringList QgsProviderRegistry::providerList() const
00516 {
00517   QStringList lst;
00518   for ( Providers::const_iterator it = mProviders.begin(); it != mProviders.end(); it++ )
00519   {
00520     lst.append( it->first );
00521   }
00522   return lst;
00523 }
00524 
00525 
00526 const QgsProviderMetadata* QgsProviderRegistry::providerMetadata( const QString& providerKey ) const
00527 {
00528   return findMetadata_( mProviders, providerKey );
00529 }
00530 
00531 
00532 /*
00533 QgsDataProvider *
00534 QgsProviderRegistry::openVector( QString const & dataSource, QString const & providerKey )
00535 {
00536     return getProvider( providerKey, dataSource );
00537 } // QgsProviderRegistry::openVector
00538 */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines