00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "typepluginloader_p.h"
00022
00023 #include "item.h"
00024 #include "itemserializer_p.h"
00025 #include "itemserializerplugin.h"
00026
00027
00028 #include <kdebug.h>
00029 #include <kmimetype.h>
00030
00031
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
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
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
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();
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
00145 if ( const PluginEntry * pe = plugin( DEFAULT_NAME ) )
00146 return pe;
00147
00148
00149
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
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
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;
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, 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 const QString mimeType = rx.cap(1);
00244 const QByteArray classType = rx.cap(2).toLatin1();
00245 QMap<QString,MimeTypeEntry>::iterator it = map.find( mimeType );
00246 if ( it == map.end() )
00247 it = map.insert( mimeType, MimeTypeEntry( mimeType ) );
00248 it->add( classType, PluginEntry( name ) );
00249 } else {
00250 kDebug() << "ItemSerializerPluginLoader: "
00251 << "name" << name << "doesn't look like mimetype@classtype" << endl;
00252 }
00253 const QString APPLICATION_OCTETSTREAM = QLatin1String( _APPLICATION_OCTETSTREAM );
00254 QMap<QString,MimeTypeEntry>::iterator it = map.find( APPLICATION_OCTETSTREAM );
00255 if ( it == map.end() )
00256 it = map.insert( APPLICATION_OCTETSTREAM, MimeTypeEntry( APPLICATION_OCTETSTREAM ) );
00257 it->add( "QByteArray", mDefaultPlugin );
00258 it->add( LEGACY_NAME, mDefaultPlugin );
00259 const int size = map.size();
00260 allMimeTypes.reserve( size );
00261 std::copy( map.begin(), map.end(),
00262 std::back_inserter( allMimeTypes ) );
00263 }
00264
00265 QObject * findBestMatch( const QString & type, const QVector<int> & metaTypeId, TypePluginLoader::Options opt ) {
00266 if ( QObject * const plugin = findBestMatch( type, metaTypeId ) ) {
00267 if ( ( opt & TypePluginLoader::NoDefault ) && plugin == mDefaultPlugin.plugin() )
00268 return 0;
00269 return plugin;
00270 }
00271 return 0;
00272 }
00273
00274 QObject * findBestMatch( const QString & type, const QVector<int> & metaTypeIds ) {
00275 if ( QObject * const plugin = cacheLookup( type, metaTypeIds ) )
00276 // plugin cached, so let's take that one
00277 return plugin;
00278 int chosen = -1;
00279 QObject * const plugin = findBestMatchImpl( type, metaTypeIds, chosen );
00280 if ( metaTypeIds.empty() )
00281 if ( plugin )
00282 cachedDefaultPlugins[type] = plugin;
00283 if ( chosen >= 0 )
00284 cachedPlugins[type][chosen] = plugin;
00285 return plugin;
00286 }
00287
00288 private:
00289 QObject * findBestMatchImpl( const QString &type, const QVector<int> & metaTypeIds, int & chosen ) const
00290 {
00291 KMimeType::Ptr mimeType = KMimeType::mimeType( type, KMimeType::ResolveAliases );
00292 if ( mimeType.isNull() )
00293 return mDefaultPlugin.plugin();
00294
00295
00296 QVector<int> matchingIndexes;
00297 for ( int i = 0, end = allMimeTypes.size(); i < end; ++i ) {
00298 if ( mimeType->is( allMimeTypes[i].type() ) )
00299 matchingIndexes.append( i );
00300 }
00301
00302
00303 QVector<int> order;
00304 if ( matchingIndexes.size() <= 1 ) {
00305 order.push_back( 0 );
00306 } else {
00307 boost::adjacency_list<> graph( matchingIndexes.size() );
00308 for ( int i = 0, end = matchingIndexes.size() ; i != end ; ++i ) {
00309 KMimeType::Ptr mimeType = KMimeType::mimeType( allMimeTypes[matchingIndexes[i]].type(), KMimeType::ResolveAliases );
00310 if ( mimeType.isNull() )
00311 continue;
00312 for ( int j = 0; j != end; ++j ) {
00313 if ( i != j && mimeType->is( allMimeTypes[matchingIndexes[j]].type() ) )
00314 boost::add_edge( j, i, graph );
00315 }
00316 }
00317
00318 order.reserve( matchingIndexes.size() );
00319 try {
00320 boost::topological_sort( graph, std::back_inserter( order ) );
00321 } catch ( boost::not_a_dag &e ) {
00322 kWarning() << "Mimetype tree is not a DAG!";
00323 return mDefaultPlugin.plugin();
00324 }
00325 }
00326
00327
00328 kDebug() << "Looking for " << format( type, metaTypeIds );
00329 for ( QVector<int>::const_iterator it = order.constBegin(), end = order.constEnd() ; it != end ; ++it ) {
00330 kDebug() << " Considering serializer plugin for type" << allMimeTypes[matchingIndexes[*it]].type()
00331 << "as the closest match";
00332 const MimeTypeEntry & mt = allMimeTypes[matchingIndexes[*it]];
00333 if ( metaTypeIds.empty() ) {
00334 if ( const PluginEntry * const entry = mt.defaultPlugin() ) {
00335 kDebug() << " -> got " << entry->pluginClassName() << " and am happy with it.";
00336 return entry->plugin();
00337 } else {
00338 kDebug() << " -> no default plugin for this mime type, trying next";
00339 }
00340 } else if ( const PluginEntry * const entry = mt.plugin( metaTypeIds, chosen ) ) {
00341 kDebug() << " -> got " << entry->pluginClassName() << " and am happy with it.";
00342 return entry->plugin();
00343 } else {
00344 kDebug() << " -> can't handle any of the types, trying next";
00345 }
00346 }
00347
00348 kDebug() << " No further candidates, using default plugin";
00349
00350 return mDefaultPlugin.plugin();
00351 }
00352
00353 std::vector<MimeTypeEntry> allMimeTypes;
00354 QHash<QString, QMap<int,QObject*> > cachedPlugins;
00355 QHash<QString, QObject*> cachedDefaultPlugins;
00356
00357
00358 QObject * cacheLookup( const QString & mimeType, const QVector<int> & metaTypeIds ) const {
00359 if ( metaTypeIds.empty() ) {
00360 const QHash<QString,QObject*>::const_iterator hit = cachedDefaultPlugins.find( mimeType );
00361 if ( hit != cachedDefaultPlugins.end() )
00362 return *hit;
00363 }
00364
00365 const QHash<QString,QMap<int,QObject*> >::const_iterator hit = cachedPlugins.find( mimeType );
00366 if ( hit == cachedPlugins.end() )
00367 return 0;
00368 bool sawZero = false;
00369 for ( QVector<int>::const_iterator it = metaTypeIds.begin(), end = metaTypeIds.end() ; it != end ; ++it )
00370 if ( *it == 0 )
00371 sawZero = true;
00372 else if ( QObject * const o = hit->value( *it ) )
00373 return o;
00374 if ( sawZero )
00375 return hit->value( 0 );
00376 return 0;
00377 }
00378
00379 private:
00380 PluginEntry mDefaultPlugin;
00381 };
00382
00383 K_GLOBAL_STATIC( PluginRegistry, s_pluginRegistry )
00384
00385 QObject* TypePluginLoader::objectForMimeTypeAndClass( const QString &mimetype, const QVector<int> & metaTypeIds, Options opt )
00386 {
00387 return s_pluginRegistry->findBestMatch( mimetype, metaTypeIds, opt );
00388 }
00389
00390 #if 0
00391 QObject* TypePluginLoader::legacyObjectForMimeType( const QString &mimetype ) {
00392
00393 return objectForMimeTypeAndClass( mimetype, QVector<int>( 1, 0 ) );
00394 }
00395 #endif
00396
00397 QObject* TypePluginLoader::defaultObjectForMimeType( const QString &mimetype ) {
00398 return objectForMimeTypeAndClass( mimetype, QVector<int>() );
00399 }
00400
00401 ItemSerializerPlugin* TypePluginLoader::pluginForMimeTypeAndClass( const QString &mimetype, const QVector<int> &metaTypeIds, Options opt )
00402 {
00403 return qobject_cast<ItemSerializerPlugin*>( objectForMimeTypeAndClass( mimetype, metaTypeIds, opt ) );
00404 }
00405
00406 #if 0
00407 ItemSerializerPlugin* TypePluginLoader::legacyPluginForMimeType( const QString &mimetype ) {
00408 ItemSerializerPlugin* plugin = qobject_cast<ItemSerializerPlugin*>( legacyObjectForMimeType( mimetype ) );
00409 Q_ASSERT( plugin );
00410 return plugin;
00411 }
00412 #endif
00413
00414 ItemSerializerPlugin* TypePluginLoader::defaultPluginForMimeType( const QString &mimetype ) {
00415 ItemSerializerPlugin* plugin = qobject_cast<ItemSerializerPlugin*>( defaultObjectForMimeType( mimetype ) );
00416 Q_ASSERT( plugin );
00417 return plugin;
00418 }
00419
00420
00421 }