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