akonadi
typepluginloader.cpp
00001 /* 00002 Copyright (c) 2007 Till Adam <adam@kde.org> 00003 Copyright (c) 2007 Volker Krause <vkrause@kde.org> 00004 00005 This library is free software; you can redistribute it and/or modify it 00006 under the terms of the GNU Library General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or (at your 00008 option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, but WITHOUT 00011 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00012 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00013 License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to the 00017 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00018 02110-1301, USA. 00019 */ 00020 00021 #include "typepluginloader_p.h" 00022 00023 #include "item.h" 00024 #include "itemserializer_p.h" 00025 #include "itemserializerplugin.h" 00026 00027 // KDE core 00028 #include <kdebug.h> 00029 #include <kmimetype.h> 00030 00031 // Qt 00032 #include <QtCore/QHash> 00033 #include <QtCore/QString> 00034 #include <QtCore/QByteArray> 00035 #include <QtCore/QStringList> 00036 00037 #include <boost/graph/adjacency_list.hpp> 00038 #include <boost/graph/topological_sort.hpp> 00039 00040 // temporary 00041 #include "pluginloader_p.h" 00042 00043 #include <vector> 00044 #include <cassert> 00045 00046 static const char LEGACY_NAME[] = "legacy"; 00047 static const char DEFAULT_NAME[] = "default"; 00048 static const char _APPLICATION_OCTETSTREAM[] = "application/octet-stream"; 00049 00050 namespace Akonadi { 00051 00052 K_GLOBAL_STATIC( DefaultItemSerializerPlugin, s_defaultItemSerializerPlugin ) 00053 00054 class PluginEntry 00055 { 00056 public: 00057 PluginEntry() 00058 : mPlugin( 0 ) 00059 { 00060 } 00061 00062 explicit PluginEntry( const QString &identifier, QObject *plugin = 0 ) 00063 : mIdentifier( identifier ), mPlugin( plugin ) 00064 { 00065 } 00066 00067 QObject* plugin() const 00068 { 00069 if ( mPlugin ) 00070 return mPlugin; 00071 00072 QObject *object = PluginLoader::self()->createForName( mIdentifier ); 00073 if ( !object ) { 00074 kWarning() << "ItemSerializerPluginLoader: " 00075 << "plugin" << mIdentifier << "is not valid!" << endl; 00076 00077 // we try to use the default in that case 00078 mPlugin = s_defaultItemSerializerPlugin; 00079 } 00080 00081 mPlugin = object; 00082 if ( !qobject_cast<ItemSerializerPlugin*>( mPlugin ) ) { 00083 kWarning() << "ItemSerializerPluginLoader: " 00084 << "plugin" << mIdentifier << "doesn't provide interface ItemSerializerPlugin!" << endl; 00085 00086 // we try to use the default in that case 00087 mPlugin = s_defaultItemSerializerPlugin; 00088 } 00089 00090 Q_ASSERT( mPlugin ); 00091 00092 return mPlugin; 00093 } 00094 00095 const char * pluginClassName() const 00096 { 00097 return plugin()->metaObject()->className(); 00098 } 00099 00100 QString identifier() const 00101 { 00102 return mIdentifier; 00103 } 00104 00105 bool operator<( const PluginEntry &other ) const 00106 { 00107 return mIdentifier < other.mIdentifier; 00108 } 00109 00110 bool operator<( const QString &identifier ) const 00111 { 00112 return mIdentifier < identifier; 00113 } 00114 00115 private: 00116 QString mIdentifier; 00117 mutable QObject *mPlugin; 00118 }; 00119 00120 static bool operator<( const QString &identifier, const PluginEntry &entry ) 00121 { 00122 return identifier < entry.identifier(); 00123 } 00124 00125 class MimeTypeEntry 00126 { 00127 public: 00128 explicit MimeTypeEntry( const QString & mimeType ) 00129 : m_mimeType( mimeType ), m_plugins(), m_pluginsByMetaTypeId() {} 00130 00131 QString type() const { return m_mimeType; } 00132 00133 void add( const QByteArray & class_, const PluginEntry & entry ) { 00134 m_pluginsByMetaTypeId.clear(); // iterators will be invalidated by next line 00135 m_plugins.insert( class_, entry ); 00136 } 00137 00138 const PluginEntry * plugin( const QByteArray & class_ ) const { 00139 const QHash<QByteArray,PluginEntry>::const_iterator it = m_plugins.find( class_ ); 00140 return it == m_plugins.end() ? 0 : it.operator->() ; 00141 } 00142 00143 const PluginEntry * defaultPlugin() const { 00144 // 1. If there's an explicit default plugin, use that one: 00145 if ( const PluginEntry * pe = plugin( DEFAULT_NAME ) ) 00146 return pe; 00147 00148 // 2. Otherwise, look through the already instantiated plugins, 00149 // and return one of them (preferably not the legacy one): 00150 bool sawZero = false; 00151 for ( QMap<int,QHash<QByteArray,PluginEntry>::const_iterator>::const_iterator it = m_pluginsByMetaTypeId.constBegin(), end = m_pluginsByMetaTypeId.constEnd() ; it != end ; ++it ) 00152 if ( it.key() == 0 ) 00153 sawZero = true; 00154 else 00155 if ( *it != m_plugins.end() ) 00156 return it->operator->(); 00157 00158 // 3. Otherwise, look through the whole list (again, preferably not the legacy one): 00159 for ( QHash<QByteArray,PluginEntry>::const_iterator it = m_plugins.constBegin(), end = m_plugins.constEnd() ; it != end ; ++it ) 00160 if ( it.key() == LEGACY_NAME ) 00161 sawZero = true; 00162 else 00163 return it.operator->() ; 00164 00165 // 4. take the legacy one: 00166 if ( sawZero ) 00167 return plugin( 0 ); 00168 return 0; 00169 } 00170 00171 const PluginEntry * plugin( int metaTypeId ) const { 00172 const QMap<int,QHash<QByteArray,PluginEntry>::const_iterator> & c_pluginsByMetaTypeId = m_pluginsByMetaTypeId; 00173 QMap<int,QHash<QByteArray,PluginEntry>::const_iterator>::const_iterator it = c_pluginsByMetaTypeId.find( metaTypeId ); 00174 if ( it == c_pluginsByMetaTypeId.end() ) 00175 it = QMap<int,QHash<QByteArray,PluginEntry>::const_iterator>::const_iterator( m_pluginsByMetaTypeId.insert( metaTypeId, m_plugins.find( metaTypeId ? QMetaType::typeName( metaTypeId ) : LEGACY_NAME ) ) ); 00176 return *it == m_plugins.end() ? 0 : it->operator->() ; 00177 } 00178 00179 const PluginEntry * plugin( const QVector<int> & metaTypeIds, int & chosen ) const { 00180 bool sawZero = false; 00181 for ( QVector<int>::const_iterator it = metaTypeIds.begin(), end = metaTypeIds.end() ; it != end ; ++it ) 00182 if ( *it == 0 ) { 00183 sawZero = true; // skip the legacy type and see if we can find something else first 00184 } else if ( const PluginEntry * const entry = plugin( *it ) ) { 00185 chosen = *it; 00186 return entry; 00187 } 00188 if ( sawZero ) { 00189 chosen = 0; 00190 return plugin( 0 ); 00191 } 00192 return 0; 00193 } 00194 00195 private: 00196 QString m_mimeType; 00197 QHash< QByteArray/* class */, PluginEntry > m_plugins; 00198 mutable QMap<int,QHash<QByteArray,PluginEntry>::const_iterator> m_pluginsByMetaTypeId; 00199 }; 00200 00201 static bool operator<( const MimeTypeEntry & lhs, const MimeTypeEntry & rhs ) 00202 { 00203 return lhs.type() < rhs.type() ; 00204 } 00205 00206 static bool operator<( const MimeTypeEntry & lhs, const QString & rhs ) 00207 { 00208 return lhs.type() < rhs ; 00209 } 00210 00211 static bool operator<( const QString & lhs, const MimeTypeEntry & rhs ) 00212 { 00213 return lhs < rhs.type(); 00214 } 00215 00216 static QString format( const QString & mimeType, const QVector<int> & metaTypeIds ) { 00217 if ( metaTypeIds.empty() ) 00218 return QLatin1String( "default for " ) + mimeType; 00219 QStringList classTypes; 00220 Q_FOREACH( int metaTypeId, metaTypeIds ) 00221 classTypes.push_back( QString::fromLatin1( metaTypeId ? QMetaType::typeName( metaTypeId ) : LEGACY_NAME ) ); 00222 return mimeType + QLatin1String("@{") + classTypes.join(QLatin1String(",")) + QLatin1Char('}'); 00223 } 00224 00225 class PluginRegistry 00226 { 00227 public: 00228 PluginRegistry() 00229 : mDefaultPlugin( PluginEntry( QLatin1String( "application/octet-stream@QByteArray" ), s_defaultItemSerializerPlugin ) ) 00230 { 00231 const PluginLoader* pl = PluginLoader::self(); 00232 if ( !pl ) { 00233 kWarning() << "Cannot instantiate plugin loader!" << endl; 00234 return; 00235 } 00236 const QStringList names = pl->names(); 00237 kDebug() << "ItemSerializerPluginLoader: " 00238 << "found" << names.size() << "plugins." << endl; 00239 QMap<QString,MimeTypeEntry> map; 00240 QRegExp rx( QLatin1String( "(.+)@(.+)" ) ); 00241 Q_FOREACH ( const QString & name, names ) 00242 if ( rx.exactMatch( name ) ) { 00243 KMimeType::Ptr mime = KMimeType::mimeType( rx.cap(1), KMimeType::ResolveAliases ); 00244 if ( mime ) { 00245 const QString mimeType = mime->name(); 00246 const QByteArray classType = rx.cap(2).toLatin1(); 00247 QMap<QString,MimeTypeEntry>::iterator it = map.find( mimeType ); 00248 if ( it == map.end() ) 00249 it = map.insert( mimeType, MimeTypeEntry( mimeType ) ); 00250 it->add( classType, PluginEntry( name ) ); 00251 } 00252 } else { 00253 kDebug() << "ItemSerializerPluginLoader: " 00254 << "name" << name << "doesn't look like mimetype@classtype" << endl; 00255 } 00256 const QString APPLICATION_OCTETSTREAM = QLatin1String( _APPLICATION_OCTETSTREAM ); 00257 QMap<QString,MimeTypeEntry>::iterator it = map.find( APPLICATION_OCTETSTREAM ); 00258 if ( it == map.end() ) 00259 it = map.insert( APPLICATION_OCTETSTREAM, MimeTypeEntry( APPLICATION_OCTETSTREAM ) ); 00260 it->add( "QByteArray", mDefaultPlugin ); 00261 it->add( LEGACY_NAME, mDefaultPlugin ); 00262 const int size = map.size(); 00263 allMimeTypes.reserve( size ); 00264 std::copy( map.begin(), map.end(), 00265 std::back_inserter( allMimeTypes ) ); 00266 } 00267 00268 QObject * findBestMatch( const QString & type, const QVector<int> & metaTypeId, TypePluginLoader::Options opt ) { 00269 if ( QObject * const plugin = findBestMatch( type, metaTypeId ) ) { 00270 if ( ( opt & TypePluginLoader::NoDefault ) && plugin == mDefaultPlugin.plugin() ) 00271 return 0; 00272 return plugin; 00273 } 00274 return 0; 00275 } 00276 00277 QObject * findBestMatch( const QString & type, const QVector<int> & metaTypeIds ) { 00278 if ( QObject * const plugin = cacheLookup( type, metaTypeIds ) ) 00279 // plugin cached, so let's take that one 00280 return plugin; 00281 int chosen = -1; 00282 QObject * const plugin = findBestMatchImpl( type, metaTypeIds, chosen ); 00283 if ( metaTypeIds.empty() ) 00284 if ( plugin ) 00285 cachedDefaultPlugins[type] = plugin; 00286 if ( chosen >= 0 ) 00287 cachedPlugins[type][chosen] = plugin; 00288 return plugin; 00289 } 00290 00291 private: 00292 QObject * findBestMatchImpl( const QString &type, const QVector<int> & metaTypeIds, int & chosen ) const 00293 { 00294 KMimeType::Ptr mimeType = KMimeType::mimeType( type, KMimeType::ResolveAliases ); 00295 if ( mimeType.isNull() ) 00296 return mDefaultPlugin.plugin(); 00297 00298 // step 1: find all plugins that match at all 00299 QVector<int> matchingIndexes; 00300 for ( int i = 0, end = allMimeTypes.size(); i < end; ++i ) { 00301 if ( mimeType->is( allMimeTypes[i].type() ) ) 00302 matchingIndexes.append( i ); 00303 } 00304 00305 // step 2: if we have more than one match, find the most specific one using topological sort 00306 QVector<int> order; 00307 if ( matchingIndexes.size() <= 1 ) { 00308 order.push_back( 0 ); 00309 } else { 00310 boost::adjacency_list<> graph( matchingIndexes.size() ); 00311 for ( int i = 0, end = matchingIndexes.size() ; i != end ; ++i ) { 00312 KMimeType::Ptr mimeType = KMimeType::mimeType( allMimeTypes[matchingIndexes[i]].type(), KMimeType::ResolveAliases ); 00313 if ( mimeType.isNull() ) 00314 continue; 00315 for ( int j = 0; j != end; ++j ) { 00316 if ( i != j && mimeType->is( allMimeTypes[matchingIndexes[j]].type() ) ) 00317 boost::add_edge( j, i, graph ); 00318 } 00319 } 00320 00321 order.reserve( matchingIndexes.size() ); 00322 try { 00323 boost::topological_sort( graph, std::back_inserter( order ) ); 00324 } catch ( boost::not_a_dag &e ) { 00325 kWarning() << "Mimetype tree is not a DAG!"; 00326 return mDefaultPlugin.plugin(); 00327 } 00328 } 00329 00330 // step 3: ask each one in turn if it can handle any of the metaTypeIds: 00331 // kDebug() << "Looking for " << format( type, metaTypeIds ); 00332 for ( QVector<int>::const_iterator it = order.constBegin(), end = order.constEnd() ; it != end ; ++it ) { 00333 // kDebug() << " Considering serializer plugin for type" << allMimeTypes[matchingIndexes[*it]].type() 00334 // // << "as the closest match"; 00335 const MimeTypeEntry & mt = allMimeTypes[matchingIndexes[*it]]; 00336 if ( metaTypeIds.empty() ) { 00337 if ( const PluginEntry * const entry = mt.defaultPlugin() ) { 00338 // kDebug() << " -> got " << entry->pluginClassName() << " and am happy with it."; 00339 return entry->plugin(); 00340 } else { 00341 // kDebug() << " -> no default plugin for this mime type, trying next"; 00342 } 00343 } else if ( const PluginEntry * const entry = mt.plugin( metaTypeIds, chosen ) ) { 00344 // kDebug() << " -> got " << entry->pluginClassName() << " and am happy with it."; 00345 return entry->plugin(); 00346 } else { 00347 // kDebug() << " -> can't handle any of the types, trying next"; 00348 } 00349 } 00350 00351 // kDebug() << " No further candidates, using default plugin"; 00352 // no luck? Use the default plugin 00353 return mDefaultPlugin.plugin(); 00354 } 00355 00356 std::vector<MimeTypeEntry> allMimeTypes; 00357 QHash<QString, QMap<int,QObject*> > cachedPlugins; 00358 QHash<QString, QObject*> cachedDefaultPlugins; 00359 00360 // ### cache NULLs, too 00361 QObject * cacheLookup( const QString & mimeType, const QVector<int> & metaTypeIds ) const { 00362 if ( metaTypeIds.empty() ) { 00363 const QHash<QString,QObject*>::const_iterator hit = cachedDefaultPlugins.find( mimeType ); 00364 if ( hit != cachedDefaultPlugins.end() ) 00365 return *hit; 00366 } 00367 00368 const QHash<QString,QMap<int,QObject*> >::const_iterator hit = cachedPlugins.find( mimeType ); 00369 if ( hit == cachedPlugins.end() ) 00370 return 0; 00371 bool sawZero = false; 00372 for ( QVector<int>::const_iterator it = metaTypeIds.begin(), end = metaTypeIds.end() ; it != end ; ++it ) 00373 if ( *it == 0 ) 00374 sawZero = true; // skip the legacy type and see if we can find something else first 00375 else if ( QObject * const o = hit->value( *it ) ) 00376 return o; 00377 if ( sawZero ) 00378 return hit->value( 0 ); 00379 return 0; 00380 } 00381 00382 private: 00383 PluginEntry mDefaultPlugin; 00384 }; 00385 00386 K_GLOBAL_STATIC( PluginRegistry, s_pluginRegistry ) 00387 00388 QObject* TypePluginLoader::objectForMimeTypeAndClass( const QString &mimetype, const QVector<int> & metaTypeIds, Options opt ) 00389 { 00390 return s_pluginRegistry->findBestMatch( mimetype, metaTypeIds, opt ); 00391 } 00392 00393 #if 0 00394 QObject* TypePluginLoader::legacyObjectForMimeType( const QString &mimetype ) { 00395 // ### impl specifically - only works b/c vector isn't used in impl ### 00396 return objectForMimeTypeAndClass( mimetype, QVector<int>( 1, 0 ) ); 00397 } 00398 #endif 00399 00400 QObject* TypePluginLoader::defaultObjectForMimeType( const QString &mimetype ) { 00401 return objectForMimeTypeAndClass( mimetype, QVector<int>() ); 00402 } 00403 00404 ItemSerializerPlugin* TypePluginLoader::pluginForMimeTypeAndClass( const QString &mimetype, const QVector<int> &metaTypeIds, Options opt ) 00405 { 00406 return qobject_cast<ItemSerializerPlugin*>( objectForMimeTypeAndClass( mimetype, metaTypeIds, opt ) ); 00407 } 00408 00409 #if 0 00410 ItemSerializerPlugin* TypePluginLoader::legacyPluginForMimeType( const QString &mimetype ) { 00411 ItemSerializerPlugin* plugin = qobject_cast<ItemSerializerPlugin*>( legacyObjectForMimeType( mimetype ) ); 00412 Q_ASSERT( plugin ); 00413 return plugin; 00414 } 00415 #endif 00416 00417 ItemSerializerPlugin* TypePluginLoader::defaultPluginForMimeType( const QString &mimetype ) { 00418 ItemSerializerPlugin* plugin = qobject_cast<ItemSerializerPlugin*>( defaultObjectForMimeType( mimetype ) ); 00419 Q_ASSERT( plugin ); 00420 return plugin; 00421 } 00422 00423 00424 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Apr 30 2012 21:49:16 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Apr 30 2012 21:49:16 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.