Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

opluginloader.cpp

Go to the documentation of this file.
00001 /*
00002  * LGPLv2 or later
00003  * zecke@handhelds.org
00004  */
00005 
00006 #include "opluginloader.h"
00007 #include "oconfig.h"
00008 #include "odebug.h"
00009 
00010 #include <qpe/qpeapplication.h>
00011 
00012 #include <qdir.h>
00013 #include <qdict.h>
00014 #include <qtl.h>
00015 #include <qfile.h>
00016 
00017 #include <stdlib.h>
00018 
00019 
00020 
00021 namespace Opie {
00022 namespace Core {
00023 namespace Internal {
00024 struct OPluginLibraryHolder {
00025     static OPluginLibraryHolder *self();
00026     QLibrary *ref( const QString& );
00027     void deref( QLibrary* );
00028 private:
00029 
00030     OPluginLibraryHolder();
00031     ~OPluginLibraryHolder();
00032     QDict<QLibrary> m_libs;
00033     static OPluginLibraryHolder* m_self;
00034 };
00035 
00036     OPluginLibraryHolder* OPluginLibraryHolder::m_self  = 0;
00037     OPluginLibraryHolder* OPluginLibraryHolder::self() {
00038         if ( !m_self )
00039             m_self = new OPluginLibraryHolder;
00040 
00041         return m_self;
00042     }
00043 
00044     OPluginLibraryHolder::OPluginLibraryHolder() {}
00045     OPluginLibraryHolder::~OPluginLibraryHolder() {}
00046 
00047     /*
00048      * We do simple ref counting... We will add the QLibrary again
00049      * and again to the dictionary and on deref we will pop and pop it
00050      * until there are no more library and we will unload and delete the library
00051      * luckily dlopen does some ref counting as well so we don't need
00052      * to hack QPEApplication
00053      */
00054     QLibrary* OPluginLibraryHolder::ref(const QString& str) {
00055         QLibrary *lib = m_libs[str];
00056 
00057         /* if not in the dict try to load it */
00058         if ( !lib ) {
00059             lib = new QLibrary( str, QLibrary::Immediately );
00060             if ( !lib->isLoaded() ) {
00061                 delete lib;
00062                 return 0l;
00063             }
00064         }
00065 
00066         /* now refcount one up */
00067         m_libs.insert( str, lib );
00068         return lib;
00069     }
00070 
00071     /*
00072      * 'unshadow' the items until we're the last then unload and delete
00073      */
00074     void OPluginLibraryHolder::deref( QLibrary* lib ) {
00075         if ( !lib )
00076             return;
00077 
00078         QString str = lib->library();
00079         /* no need to check if the lib was inserted or such */
00080         (void) m_libs.take( str );
00081         if ( !m_libs[str] ) {
00082             lib->unload();
00083             delete lib;
00084         }
00085     }
00086 }
00087 
00092 bool operator<( const OPluginItem& l, const OPluginItem& r ) {
00093     return l.position() > r.position();
00094 }
00095 
00096 bool operator>( const OPluginItem& l, const OPluginItem& r ) {
00097     return l.position() < r.position();
00098 }
00099 
00100 bool operator<=( const OPluginItem& l, const OPluginItem& r ) {
00101     return l.position() >=  r.position();
00102 }
00103 
00110 OPluginItem::OPluginItem()
00111     : m_pos( -1 ) {
00112 }
00113 
00128 OPluginItem::OPluginItem( const QString& name, const QString& path, bool b, int pos )
00129     : m_name( name ), m_path( path ), m_enabled( b ), m_pos( pos )
00130 {}
00131 
00135 OPluginItem::~OPluginItem() {
00136 }
00137 
00146 bool OPluginItem::isEmpty()const {
00147     if ( m_pos != -1 )       return false;
00148     if ( m_enabled )         return false;
00149     if ( !m_name.isEmpty() ) return false;
00150     if ( !m_path.isEmpty() ) return false;
00151 
00152     return true;
00153 }
00154 
00159 bool OPluginItem::operator==( const OPluginItem& r )const{
00160     if ( m_pos     != r.m_pos    ) return false;
00161     if ( m_enabled != r.m_enabled) return false;
00162     if ( m_name    != r.m_name   ) return false;
00163     if ( m_path    != r.m_path   ) return false;
00164     return true;
00165 }
00166 
00171 bool OPluginItem::operator!=( const OPluginItem& r )const{
00172     return !( *this == r );
00173 }
00174 
00179 QString OPluginItem::name()const {
00180     return m_name;
00181 }
00182 
00186 QString OPluginItem::path()const {
00187     return m_path;
00188 }
00189 
00193 bool OPluginItem::isEnabled()const {
00194     return m_enabled;
00195 }
00196 
00207 int OPluginItem::position()const{
00208     return m_pos;
00209 }
00210 
00216 void OPluginItem::setName(  const QString& name ) {
00217     m_name = name;
00218 }
00219 
00225 void OPluginItem::setPath( const QString& name ) {
00226     m_path = name;
00227 }
00228 
00237 void OPluginItem::setEnabled(  bool enabled ) {
00238     m_enabled = enabled;
00239 }
00240 
00248 void OPluginItem::setPosition( int pos ) {
00249     m_pos = pos;
00250 }
00251 
00252 
00253 
00281 OGenericPluginLoader::OGenericPluginLoader( const QString& name,  bool isSorted)
00282     : m_dir( name ), m_autoDelete( false ), m_isSafeMode( false ),
00283       m_isSorted( isSorted )
00284 {
00285     setPluginDir( QPEApplication::qpeDir() + "plugins/"+name );
00286     readConfig();
00287 }
00288 
00289 
00296 OGenericPluginLoader::~OGenericPluginLoader() {
00297     if ( m_autoDelete )
00298         clear();
00299 }
00300 
00309 void OGenericPluginLoader::setAutoDelete( bool t ) {
00310     m_autoDelete = t;
00311 }
00312 
00316 bool OGenericPluginLoader::autoDelete()const{
00317     return m_autoDelete;
00318 }
00319 
00326 void OGenericPluginLoader::clear() {
00327     QPtrDictIterator<QLibrary> it( m_library );
00328     for ( ;it.current(); )
00329         unload( static_cast<QUnknownInterface*>( it.currentKey() ) );
00330 }
00331 
00340 void OGenericPluginLoader::unload( QUnknownInterface* iface ) {
00341     if ( !iface )
00342         return;
00343 
00344     iface->release();
00345     Internal::OPluginLibraryHolder::self()->deref( m_library.take( iface ) );
00346 }
00347 
00354 QString OGenericPluginLoader::name()const {
00355     return m_dir;
00356 }
00357 
00358 
00367 bool OGenericPluginLoader::isInSafeMode()const {
00368     return m_isSafeMode;
00369 }
00370 
00371 
00379 OPluginItem::List OGenericPluginLoader::allAvailable( bool sorted )const {
00380     OPluginItem::List lst;
00381     for ( QStringList::ConstIterator it = m_plugDirs.begin(); it != m_plugDirs.end(); ++it )
00382         lst += plugins(  *it, sorted, false );
00383 
00384     if ( sorted )
00385         qHeapSort( lst );
00386     return lst;
00387 }
00388 
00395 OPluginItem::List OGenericPluginLoader::filtered( bool sorted )const {
00396     OPluginItem::List lst;
00397     for ( QStringList::ConstIterator it = m_plugDirs.begin(); it != m_plugDirs.end(); ++it )
00398         lst += plugins(  *it, sorted, true );
00399 
00400     if ( sorted )
00401         qHeapSort( lst );
00402     return lst;
00403 }
00404 
00405 
00417 QUnknownInterface* OGenericPluginLoader::load( const OPluginItem& item, const QUuid& uuid) {
00418     /*
00419      * Check if there could be a library
00420      */
00421     QString pa = item.path();
00422     if ( pa.isEmpty() )
00423         return 0l;
00424 
00425     /*
00426      * See if we get a library
00427      * return if we've none
00428      */
00429     setSafeMode( pa, true );
00430     QLibrary *lib = Internal::OPluginLibraryHolder::self()->ref( pa );
00431     if ( !lib ) {
00432         setSafeMode();
00433         return 0l;
00434     }
00435 
00439     QUnknownInterface*  iface=0;
00440     if ( lib->queryInterface(  uuid,  &iface ) == QS_OK ) {
00441         installTranslators( item.name() );
00442         m_library.insert( iface, lib );
00443     }else
00444         iface = 0;
00445 
00446     setSafeMode();
00447 
00448     return iface;
00449 }
00450 
00454 void OGenericPluginLoader::readConfig() {
00455 
00456 
00457 /* read the config for SafeMode */
00458     OConfig conf(  m_dir + "-odpplugins" );
00459     conf.setGroup( "General" );
00460     m_isSafeMode = conf.readBoolEntry( "SafeMode", false );
00461 }
00462 
00466 void OGenericPluginLoader::setSafeMode(const QString& str, bool b) {
00467     OConfig conf(  m_dir + "-odpplugins" );
00468     conf.setGroup( "General" );
00469     conf.writeEntry( "SafeMode", b );
00470     conf.writeEntry( "CrashedPlugin", str );
00471 }
00472 
00479 void OGenericPluginLoader::setPluginDirs( const QStringList& lst ) {
00480     m_plugDirs = lst;
00481 }
00482 
00488 void OGenericPluginLoader::setPluginDir( const QString& str) {
00489     m_plugDirs.clear();
00490     m_plugDirs.append( str );
00491 }
00492 
00493 
00497 bool OGenericPluginLoader::isSorted()const{
00498     return m_isSorted;
00499 }
00500 
00501 /*
00502  * make libfoo.so.1.0.0 -> foo on UNIX
00503  * make libfoo.dylib    -> foo on MAC OS X Unix
00504  * windows is obviously missing
00505  */
00509 QString OGenericPluginLoader::unlibify( const QString& str ) {
00510     QString st = str.mid( str.find( "lib" )+3 );
00511 #ifdef Q_OS_MACX
00512     return st.left( st.findRev( ".dylib" ) );
00513 #else
00514     return st.left( st.findRev( ".so" ) );
00515 #endif
00516 }
00517 
00530 OPluginItem::List OGenericPluginLoader::plugins( const QString& _dir, bool sorted, bool disabled )const {
00531 #ifdef Q_OS_MACX
00532     QDir dir( _dir, "lib*.dylib" );
00533 #else
00534     QDir dir( _dir, "lib*.so" );
00535 #endif
00536 
00537 
00538     OPluginItem::List lst;
00539 
00540     /*
00541      * get excluded list and then iterate over them
00542      * Excluded list contains the name
00543      * Position is a list with 'name.pos.name.pos.name.pos'
00544      *
00545      * For the look up we will create two QMap<QString,pos>
00546      */
00547     QMap<QString, int> positionMap;
00548     QMap<QString, int> excludedMap;
00549 
00550 
00551     OConfig cfg( m_dir+"-odpplugins" );
00552     cfg.setGroup( _dir );
00553 
00554 
00555     QStringList excludes = cfg.readListEntry( "Excluded", ',' );
00556     for ( QStringList::Iterator it = excludes.begin(); it != excludes.end(); ++it )
00557         excludedMap.insert( *it, -2 );
00558 
00559     if ( sorted ) {
00560         QStringList pos =  cfg.readListEntry( "Positions", '.' );
00561         QStringList::Iterator it = pos.begin();
00562         QString tmp; int i;
00563         while ( it != pos.end() ) {
00564             tmp = *it++; i = (*it++).toInt();
00565             positionMap.insert(  tmp, i );
00566         }
00567 
00568     }
00569 
00570 
00571 
00572 
00573     QStringList list = dir.entryList();
00574     for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00575         QString str = unlibify( *it );
00576         OPluginItem item( str, _dir + "/" + *it );
00577 
00578         bool ex = excludedMap.contains( str );
00579         /*
00580          * if disabled but we should show all mark it as disabled
00581          * else continue because we don't want to add the item
00582          * else if sorted we assign the right position
00583          */
00584         if ( ex && !disabled)
00585             item.setEnabled( false );
00586         else if ( ex && disabled )
00587             continue;
00588         else if ( sorted )
00589             item.setPosition( positionMap[str] );
00590 
00591 
00592         lst.append( item );
00593     }
00594 
00595     return lst;
00596 }
00597 
00601 QStringList OGenericPluginLoader::languageList() {
00602     if ( m_languages.isEmpty() ) {
00603         /*
00604          * be_BY.CP1251 We will add, be_BY.CP1251,be_BY,be
00605          * to our list of languages.
00606          * Also for de_DE@euro we will add de_DE@eurp, de_DE, de
00607          * to our list of languages
00608          */
00609         QString str = ::getenv( "LANG" );
00610         m_languages += str;
00611         int pos = str.find( '@' );
00612         if( pos > 0 )
00613             m_languages += str.left( pos );
00614         
00615         
00616         pos = str.find( '.' );
00617         if ( pos > 0 )
00618             m_languages += str.left( pos );
00619 
00620         int n_pos = str.find( '_' );
00621         if ( n_pos > 0  )
00622             m_languages += str.left( n_pos );
00623 
00624     }
00625     return m_languages;
00626 }
00627 
00632 void OGenericPluginLoader::installTranslators(const QString& type) {
00633     QStringList lst = languageList();
00634 
00635     /*
00636      * for each language and maybe later for each language dir...
00637      * try to load a Translator
00638      */
00639     for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
00640         QTranslator* trans = new QTranslator(  qApp );
00641         QString tfn = QPEApplication::qpeDir()+"i18n/" + *it + "/lib" + type + ".qm" ;
00642 
00643         /*
00644          * If loaded then install else clean up and don't leak
00645          */
00646         if ( trans->load( tfn ) ) 
00647             qApp->installTranslator( trans );   
00648         else
00649             delete trans;
00650     }
00651 }
00652 
00665 OPluginLoader::OPluginLoader( const QString& name,  bool sorted )
00666     : OGenericPluginLoader( name, sorted )
00667 {
00668 }
00669 
00674 OPluginLoader::~OPluginLoader() {
00675 }
00676 
00685 OPluginManager::OPluginManager(  OGenericPluginLoader* loader)
00686     : m_loader( loader ), m_isSorted( false )
00687 {
00688 }
00689 
00700 OPluginManager::OPluginManager( const QString& name, const OPluginItem::List& lst,  bool isSorted)
00701     : m_loader( 0l ), m_cfgName( name ), m_plugins( lst ), m_isSorted( isSorted )
00702 {
00703 }
00704 
00708 OPluginManager::~OPluginManager() {
00709 }
00710 
00722 OPluginItem OPluginManager::crashedPlugin()const {
00723     return m_crashed;
00724 }
00725 
00733 OPluginItem::List OPluginManager::managedPlugins()const {
00734     return m_plugins;
00735 }
00736 
00748 void OPluginManager::setPosition( const OPluginItem& item) {
00749     replace( item );
00750 }
00751 
00761 void OPluginManager::enable(  const OPluginItem& item ) {
00762     setEnabled( item, true );
00763 }
00764 
00773 void OPluginManager::disable( const OPluginItem& item) {
00774     setEnabled( item, false );
00775 }
00776 
00788 void OPluginManager::setEnabled( const OPluginItem& _item, bool b ) {
00789     OPluginItem item = _item;
00790     item.setEnabled( b );
00791     replace( item );
00792 }
00793 
00800 void OPluginManager::load() {
00801     OConfig cfg( configName() );
00802     cfg.setGroup(  "General" );
00803     QString crashedPath = cfg.readEntry( "CrashedPlugin" );
00804 
00805     /* if we've a loader this applies if we were called from the first part */
00806     if ( m_loader )
00807         m_plugins = m_loader->allAvailable( m_loader->isSorted() );
00808 
00809     /*  fast and normal route if we did not crash... */
00810     if ( crashedPath.isEmpty() )
00811         return;
00812 
00813     /* lets try to find the plugin path and this way the associated item */
00814     for ( OPluginItem::List::Iterator it = m_plugins.begin(); it != m_plugins.end(); ++it )
00815         if (  (*it).path() ==  crashedPath ) {
00816             m_crashed = *it;
00817             break;
00818         }
00819 }
00820 
00821 
00828 void OPluginManager::save() {
00829     QMap<QString, QStringList> excluded;  // map for path to excluded name
00830     QMap<QString, QStringList> positions; // if positions matter contains splitted up by dirs
00831     bool sorted = m_loader ? m_loader->isSorted() : m_isSorted;
00832 
00833     /*
00834      * We will create some maps for the  groups to include positions a
00835      */
00836     for ( OPluginItem::List::Iterator it = m_plugins.begin(); it != m_plugins.end(); ++it ) {
00837         OPluginItem item = *it;
00838         QString path = QFileInfo(  item.path() ).dirPath(true);
00839         if ( sorted ) {
00840             positions[path].append( item.name() );
00841             positions[path].append( QString::number( item.position() ) );
00842         }
00843 
00844         if ( !item.isEnabled() )
00845             excluded[path].append( item.name() );
00846         if ( !excluded.contains( path ) )
00847             excluded[path] = QString::null;
00848 
00849     }
00850 
00851 /*
00852  * The code below wouldn't work because we can't delete groups/keys from the config
00853  * ### for ODP   make Config right!
00854  */
00855 //    if ( excluded.isEmpty() && positions.isEmpty() ) return;
00856     /*
00857      * Now safe for each path
00858      */
00859     OConfig cfg( configName() );
00860 
00861     /* safe excluded items */
00862     for ( QMap<QString, QStringList>::Iterator it = excluded.begin(); it != excluded.end(); ++it ) {
00863         OConfigGroupSaver saver( &cfg, it.key() );
00864         cfg.writeEntry("Excluded", it.data(), ',' );
00865     }
00866 
00867     /*  safe positions we could also see if positions.contains(path) and remove/write in the above loop
00868      * ### Write a Test Suite that can profile these runs...
00869      */
00870     for ( QMap<QString, QStringList>::Iterator it = positions.begin(); it != positions.end(); ++it ) {
00871         OConfigGroupSaver saver( &cfg, it.key() );
00872         cfg.writeEntry("Positions", it.data(), '.' );
00873     }
00874 }
00875 
00879 QString OPluginManager::configName()const {
00880     QString str = m_loader ? m_loader->name() : m_cfgName;
00881     return str + "-odpplugins";
00882 }
00883 
00887 void OPluginManager::replace( const OPluginItem& item ) {
00888     OPluginItem _item;
00889 
00890     /* for all plugins */
00891     for ( OPluginItem::List::Iterator it=m_plugins.begin();it != m_plugins.end(); ++it ) {
00892         _item = *it;
00893         /* if path and name are the same we will remove, readd and return */
00894         if ( _item.path() == item.path() &&
00895              _item.name() == item.name() ) {
00896             it = m_plugins.remove( it );
00897             m_plugins.append( item );
00898             return;
00899         }
00900 
00901     }
00902 }
00903 
00904 }
00905 }

Generated on Sat Nov 5 16:16:09 2005 for OPIE by  doxygen 1.4.2