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

qgpluginmanager.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** $Id: qgpluginmanager.cpp,v 1.2 2003/07/10 02:40:12 llornkcor Exp $
00003 **
00004 ** Implementation of QGPluginManager class
00005 **
00006 ** Copyright (C) 2000-2003 Trolltech AS.  All rights reserved.
00007 **
00008 ** This file is part of the tools module of the Qt GUI Toolkit.
00009 **
00010 ** This file may be distributed under the terms of the Q Public License
00011 ** as defined by Trolltech AS of Norway and appearing in the file
00012 ** LICENSE.QPL included in the packaging of this file.
00013 **
00014 ** This file may be distributed and/or modified under the terms of the
00015 ** GNU General Public License version 2 as published by the Free Software
00016 ** Foundation and appearing in the file LICENSE.GPL included in the
00017 ** packaging of this file.
00018 **
00019 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
00020 ** licenses may use this file in accordance with the Qt Commercial License
00021 ** Agreement provided with the Software.
00022 **
00023 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00024 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00025 **
00026 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
00027 **   information about Qt Commercial License Agreements.
00028 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
00029 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00030 **
00031 ** Contact info@trolltech.com if any conditions of this licensing are
00032 ** not clear to you.
00033 **
00034 **********************************************************************/
00035 
00036 #include "qgpluginmanager_p.h"
00037 #ifndef QT_NO_COMPONENT
00038 #include "qcomlibrary_p.h"
00039 #include "qmap.h"
00040 #include "qdir.h"
00041 
00042 /*
00043   The following co-occurrence code is borrowed from Qt Linguist.
00044 
00045   How similar are two texts? The approach used here relies on
00046   co-occurrence matrices and is very efficient.
00047 
00048   Let's see with an example: how similar are "here" and "hither"?  The
00049   co-occurrence matrix M for "here" is M[h,e] = 1, M[e,r] = 1,
00050   M[r,e] = 1 and 0 elsewhere; the matrix N for "hither" is N[h,i] = 1,
00051   N[i,t] = 1, ..., N[h,e] = 1, N[e,r] = 1 and 0 elsewhere.  The union
00052   U of both matrices is the matrix U[i,j] = max { M[i,j], N[i,j] },
00053   and the intersection V is V[i,j] = min { M[i,j], N[i,j] }. The score
00054   for a pair of texts is
00055 
00056       score = (sum of V[i,j] over all i, j) / (sum of U[i,j] over all i, j),
00057 
00058   a formula suggested by Arnt Gulbrandsen. Here we have
00059 
00060       score = 2 / 6,
00061 
00062   or one third.
00063 
00064   The implementation differs from this in a few details.  Most
00065   importantly, repetitions are ignored; for input "xxx", M[x,x] equals
00066   1, not 2.
00067 */
00068 
00069 /*
00070   Every character is assigned to one of 20 buckets so that the
00071   co-occurrence matrix requires only 20 * 20 = 400 bits, not
00072   256 * 256 = 65536 bits or even more if we want the whole Unicode.
00073   Which character falls in which bucket is arbitrary.
00074 
00075   The second half of the table is a replica of the first half, because of
00076   laziness.
00077 */
00078 static const char indexOf[256] = {
00079     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
00080     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
00081 //      !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
00082     0,  2,  6,  7,  10, 12, 15, 19, 2,  6,  7,  10, 12, 15, 19, 0,
00083 //  0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
00084     1,  3,  4,  5,  8,  9,  11, 13, 14, 16, 2,  6,  7,  10, 12, 15,
00085 //  @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
00086     0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  6,  10, 11, 12, 13, 14,
00087 //  P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
00088     15, 12, 16, 17, 18, 19, 2,  10, 15, 7,  19, 2,  6,  7,  10, 0,
00089 //  `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
00090     0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  6,  10, 11, 12, 13, 14,
00091 //  p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~
00092     15, 12, 16, 17, 18, 19, 2,  10, 15, 7,  19, 2,  6,  7,  10, 0,
00093 
00094     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
00095     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
00096     0,  2,  6,  7,  10, 12, 15, 19, 2,  6,  7,  10, 12, 15, 19, 0,
00097     1,  3,  4,  5,  8,  9,  11, 13, 14, 16, 2,  6,  7,  10, 12, 15,
00098     0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  6,  10, 11, 12, 13, 14,
00099     15, 12, 16, 17, 18, 19, 2,  10, 15, 7,  19, 2,  6,  7,  10, 0,
00100     0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  6,  10, 11, 12, 13, 14,
00101     15, 12, 16, 17, 18, 19, 2,  10, 15, 7,  19, 2,  6,  7,  10, 0
00102 };
00103 
00104 /*
00105   The entry bitCount[i] (for i between 0 and 255) is the number of
00106   bits used to represent i in binary.
00107 */
00108 static const char bitCount[256] = {
00109     0,  1,  1,  2,  1,  2,  2,  3,  1,  2,  2,  3,  2,  3,  3,  4,
00110     1,  2,  2,  3,  2,  3,  3,  4,  2,  3,  3,  4,  3,  4,  4,  5,
00111     1,  2,  2,  3,  2,  3,  3,  4,  2,  3,  3,  4,  3,  4,  4,  5,
00112     2,  3,  3,  4,  3,  4,  4,  5,  3,  4,  4,  5,  4,  5,  5,  6,
00113     1,  2,  2,  3,  2,  3,  3,  4,  2,  3,  3,  4,  3,  4,  4,  5,
00114     2,  3,  3,  4,  3,  4,  4,  5,  3,  4,  4,  5,  4,  5,  5,  6,
00115     2,  3,  3,  4,  3,  4,  4,  5,  3,  4,  4,  5,  4,  5,  5,  6,
00116     3,  4,  4,  5,  4,  5,  5,  6,  4,  5,  5,  6,  5,  6,  6,  7,
00117     1,  2,  2,  3,  2,  3,  3,  4,  2,  3,  3,  4,  3,  4,  4,  5,
00118     2,  3,  3,  4,  3,  4,  4,  5,  3,  4,  4,  5,  4,  5,  5,  6,
00119     2,  3,  3,  4,  3,  4,  4,  5,  3,  4,  4,  5,  4,  5,  5,  6,
00120     3,  4,  4,  5,  4,  5,  5,  6,  4,  5,  5,  6,  5,  6,  6,  7,
00121     2,  3,  3,  4,  3,  4,  4,  5,  3,  4,  4,  5,  4,  5,  5,  6,
00122     3,  4,  4,  5,  4,  5,  5,  6,  4,  5,  5,  6,  5,  6,  6,  7,
00123     3,  4,  4,  5,  4,  5,  5,  6,  4,  5,  5,  6,  5,  6,  6,  7,
00124     4,  5,  5,  6,  5,  6,  6,  7,  5,  6,  6,  7,  6,  7,  7,  8
00125 };
00126 
00127 class QCoMatrix
00128 {
00129 public:
00130     /*
00131       The matrix has 20 * 20 = 400 entries. This requires 50 bytes, or
00132       13 words. Some operations are performed on words for more
00133       efficiency.
00134     */
00135     union {
00136         Q_UINT8 b[52];
00137         Q_UINT32 w[13];
00138     };
00139 
00140     QCoMatrix() { memset( b, 0, 52 ); }
00141     QCoMatrix( const char *text ) {
00142         char c = '\0', d;
00143         memset( b, 0, 52 );
00144         while ( (d = *text) != '\0' ) {
00145             setCoocc( c, d );
00146             if ( (c = *++text) != '\0' ) {
00147                 setCoocc( d, c );
00148                 text++;
00149             }
00150         }
00151     }
00152 
00153     void setCoocc( char c, char d ) {
00154         int k = indexOf[(uchar) c] + 20 * indexOf[(uchar) d];
00155         b[k >> 3] |= k & 0x7;
00156     }
00157 
00158     int worth() const {
00159         int result = 0;
00160         for ( int i = 0; i < 50; i++ )
00161             result += bitCount[b[i]];
00162         return result;
00163     }
00164 
00165     static QCoMatrix reunion( const QCoMatrix& m, const QCoMatrix& n )
00166     {
00167         QCoMatrix p;
00168         for ( int i = 0; i < 13; i++ )
00169             p.w[i] = m.w[i] | n.w[i];
00170         return p;
00171     }
00172     static QCoMatrix intersection( const QCoMatrix& m, const QCoMatrix& n )
00173     {
00174         QCoMatrix p;
00175         for ( int i = 0; i < 13; i++ )
00176             p.w[i] = m.w[i] & n.w[i];
00177         return p;
00178     }
00179 };
00180 
00181 /*
00182   Returns an integer between 0 (dissimilar) and 15 (very similar)
00183   depending on  how similar the string is to \a target.
00184 
00185   This function is efficient, but its results might change in future
00186   versions of Qt as the algorithm evolves.
00187 
00188   \code
00189     QString s( "color" );
00190     a = similarity( s, "color" );  // a == 15
00191     a = similarity( s, "colour" ); // a == 8
00192     a = similarity( s, "flavor" ); // a == 4
00193     a = similarity( s, "dahlia" ); // a == 0
00194   \endcode
00195 */
00196 static int similarity( const QString& s1, const QString& s2 )
00197 {
00198     QCoMatrix m1( s1 );
00199     QCoMatrix m2( s2 );
00200     return ( 15 * (QCoMatrix::intersection(m1, m2).worth() + 1) ) /
00201            ( QCoMatrix::reunion(m1, m2).worth() + 1 );
00202 }
00203 
00287 #include <qptrlist.h>
00288 
00289 QGPluginManager::QGPluginManager( const QUuid& id, const QStringList& paths, const QString &suffix, bool cs )
00290     : interfaceId( id ), plugDict( 17, cs ), casesens( cs ), autounload( TRUE )
00291 {
00292     // Every QLibrary object is destroyed on destruction of the manager
00293     libDict.setAutoDelete( TRUE );
00294     for ( QStringList::ConstIterator it = paths.begin(); it != paths.end(); ++it ) {
00295         QString path = *it;
00296         addLibraryPath( path + suffix );
00297     }
00298 }
00299 
00300 QGPluginManager::~QGPluginManager()
00301 {
00302     if ( !autounload ) {
00303         QDictIterator<QLibrary> it( libDict );
00304         while ( it.current() ) {
00305             QLibrary *lib = it.current();
00306             ++it;
00307             lib->setAutoUnload( FALSE );
00308         }
00309     }
00310 }
00311 
00312 void QGPluginManager::addLibraryPath( const QString& path )
00313 {
00314     if ( !enabled() || !QDir( path ).exists( ".", TRUE ) )
00315         return;
00316 
00317 #if defined(Q_OS_WIN32)
00318     QString filter = "dll";
00319 #elif defined(Q_OS_MACX)
00320     QString filter = "dylib";
00321 #elif defined(Q_OS_UNIX)
00322     QString filter = "so";
00323 #endif
00324 
00325     QStringList plugins = QDir(path).entryList( "*." + filter );
00326     for ( QStringList::Iterator p = plugins.begin(); p != plugins.end(); ++p ) {
00327         QString lib = QDir::cleanDirPath( path + "/" + *p );
00328         if ( libList.contains( lib ) )
00329             continue;
00330         libList.append( lib );
00331     }
00332 }
00333 
00334 const QLibrary* QGPluginManager::library( const QString& feature ) const
00335 {
00336     if ( !enabled() || feature.isEmpty() )
00337         return 0;
00338 
00339     // We already have a QLibrary object for this feature
00340     QLibrary *library = 0;
00341     if ( ( library = plugDict[feature] ) )
00342         return library;
00343 
00344     // Find the filename that matches the feature request best
00345     QMap<int, QStringList> map;
00346     QStringList::ConstIterator it = libList.begin();
00347     int best = 0;
00348     int worst = 15;
00349     while ( it != libList.end() ) {
00350         if ( (*it).isEmpty() || libDict[*it] ) {
00351             ++it;
00352             continue;
00353         }
00354         QString basename = QFileInfo(*it).baseName();
00355         int s = similarity( feature, basename );
00356         if ( s < worst )
00357             worst = s;
00358         if ( s > best )
00359             best = s;
00360         map[s].append( basename + QChar(0xfffd) + *it );
00361         ++it;
00362     }
00363 
00364     if ( map.isEmpty() )
00365         return 0; // no libraries to add
00366 
00367     // Start with the best match to get the library object
00368     QGPluginManager *that = (QGPluginManager*)this;
00369     for ( int s = best; s >= worst; --s ) {
00370         QStringList group = map[s];
00371         group.sort(); // sort according to the base name
00372         QStringList::ConstIterator git = group.begin();
00373         while ( git != group.end() ) {
00374             QString lib = (*git).mid( (*git).find( QChar(0xfffd) ) + 1 );
00375             QString basename = (*git).left( (*git).find( QChar(0xfffd) ) );
00376             ++git;
00377 
00378             QStringList sameBasename;
00379             while( git != group.end() &&
00380                    basename == (*git).left( (*git).find( QChar(0xfffd) ) )  ) {
00381                 sameBasename << (*git).mid( (*git).find( QChar(0xfffd) ) + 1 );
00382                 ++git;
00383             }
00384 
00385             if ( sameBasename.isEmpty() ) {
00386                 that->addLibrary( new QComLibrary( lib ) );
00387             } else {
00388                 QPtrList<QComLibrary> same;
00389                 same.setAutoDelete( TRUE );
00390                 for ( QStringList::ConstIterator bit = sameBasename.begin();
00391                       bit != sameBasename.end(); ++bit )
00392                     same.append( new QComLibrary( *bit ) );
00393                 QComLibrary* bestMatch = 0;
00394                 for ( QComLibrary* candidate = same.first(); candidate; candidate = same.next() )
00395                     if ( candidate->qtVersion() && candidate->qtVersion() <= QT_VERSION
00396                          && ( !bestMatch || candidate->qtVersion() > bestMatch->qtVersion() ) )
00397                         bestMatch = candidate;
00398                 if ( bestMatch ) {
00399                     same.find( bestMatch );
00400                     that->addLibrary( same.take() );
00401                 }
00402             }
00403 
00404             if ( ( library = that->plugDict[feature] ) )
00405                 return library;
00406         }
00407     }
00408     return 0;
00409 }
00410 
00411 QStringList QGPluginManager::featureList() const
00412 {
00413     QStringList features;
00414 
00415     if ( !enabled() )
00416         return features;
00417 
00418     QGPluginManager *that = (QGPluginManager*)this;
00419     QStringList theLibs = libList;
00420     QStringList phase2Libs;
00421     QStringList phase2Deny;
00422 
00423     /* In order to get the feature list we need to add all interesting
00424       libraries. If there are libraries with the same base name, we
00425       prioritze the one that fits our Qt version number and ignore the
00426       others  */
00427     QStringList::Iterator it;
00428     for ( it = theLibs.begin(); it != theLibs.end(); ++it  ) {
00429         if ( (*it).isEmpty() || libDict[*it] )
00430             continue;
00431         QComLibrary* library = new QComLibrary( *it );
00432         if ( library->qtVersion() == QT_VERSION ) {
00433             that->addLibrary( library );
00434             phase2Deny << QFileInfo( *it ).baseName();
00435         } else {
00436             delete library;
00437             phase2Libs << *it;
00438         }
00439     }
00440     for ( it = phase2Libs.begin(); it != phase2Libs.end(); ++it  )
00441         if ( !phase2Deny.contains( QFileInfo( *it ).baseName() ) )
00442             that->addLibrary( new QComLibrary( *it ) );
00443 
00444     for ( QDictIterator<QLibrary> pit( plugDict ); pit.current(); ++pit )
00445         features << pit.currentKey();
00446 
00447     return features;
00448 }
00449 
00450 bool QGPluginManager::addLibrary( QLibrary* lib )
00451 {
00452     if ( !enabled() || !lib )
00453         return FALSE;
00454 
00455     QComLibrary* plugin = (QComLibrary*)lib;
00456     bool useful = FALSE;
00457 
00458     QUnknownInterface* iFace = 0;
00459     plugin->queryInterface( interfaceId, &iFace );
00460     if ( iFace ) {
00461         QFeatureListInterface *fliFace = 0;
00462         QComponentInformationInterface *cpiFace = 0;
00463         iFace->queryInterface( IID_QFeatureList, (QUnknownInterface**)&fliFace );
00464         if ( !fliFace )
00465             plugin->queryInterface( IID_QFeatureList, (QUnknownInterface**)&fliFace );
00466         if ( !fliFace ) {
00467             iFace->queryInterface( IID_QComponentInformation, (QUnknownInterface**)&cpiFace );
00468             if ( !cpiFace )
00469                 plugin->queryInterface( IID_QComponentInformation, (QUnknownInterface**)&cpiFace );
00470         }
00471         QStringList fl;
00472         if ( fliFace )
00473             // Map all found features to the library
00474             fl = fliFace->featureList();
00475         else if ( cpiFace )
00476             fl << cpiFace->name();
00477 
00478         for ( QStringList::Iterator f = fl.begin(); f != fl.end(); ++f ) {
00479             QLibrary *old = plugDict[*f];
00480             if ( !old ) {
00481                 useful = TRUE;
00482                 plugDict.replace( *f, plugin );
00483             } else {
00484                 // we have old *and* plugin, which one to pick?
00485                 QComLibrary* first = (QComLibrary*)old;
00486                 QComLibrary* second = (QComLibrary*)plugin;
00487                 bool takeFirst = TRUE;
00488                 if ( first->qtVersion() != QT_VERSION ) {
00489                     if ( second->qtVersion() == QT_VERSION )
00490                         takeFirst = FALSE;
00491                     else if ( second->qtVersion() < QT_VERSION &&
00492                               first->qtVersion() > QT_VERSION )
00493                         takeFirst = FALSE;
00494                 }
00495                 if ( !takeFirst ) {
00496                     useful = TRUE;
00497                     plugDict.replace( *f, plugin );
00498                     qWarning("%s: Discarding feature %s in %s!",
00499                              (const char*) QFile::encodeName( plugin->library()),
00500                              (*f).latin1(),
00501                              (const char*) QFile::encodeName( old->library() ) );
00502                 } else {
00503                     qWarning("%s: Feature %s already defined in %s!",
00504                              (const char*) QFile::encodeName( old->library() ),
00505                              (*f).latin1(),
00506                              (const char*) QFile::encodeName( plugin->library() ) );
00507                 }
00508             }
00509         }
00510         if ( fliFace )
00511             fliFace->release();
00512         if ( cpiFace )
00513             cpiFace->release();
00514         iFace->release();
00515     }
00516 
00517     if ( useful ) {
00518         libDict.replace( plugin->library(), plugin );
00519         if ( !libList.contains( plugin->library() ) )
00520             libList.append( plugin->library() );
00521         return TRUE;
00522     }
00523     delete plugin;
00524     return FALSE;
00525 }
00526 
00527 
00528 bool QGPluginManager::enabled() const
00529 {
00530 #ifdef QT_SHARED
00531     return TRUE;
00532 #else
00533     return FALSE;
00534 #endif
00535 }
00536 
00537 QRESULT QGPluginManager::queryUnknownInterface(const QString& feature, QUnknownInterface** iface) const
00538 {
00539     QComLibrary* plugin = 0;
00540     plugin = (QComLibrary*)library( feature );
00541     return plugin ? plugin->queryInterface( interfaceId, (QUnknownInterface**)iface ) : QE_NOINTERFACE;
00542 }
00543 
00544 #endif //QT_NO_COMPONENT

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