• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

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           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       // step 1: find all plugins that match at all
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       // step 2: if we have more than one match, find the most specific one using topological sort
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       // step 3: ask each one in turn if it can handle any of the metaTypeIds:
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       // no luck? Use the default plugin
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     // ### cache NULLs, too
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; // skip the legacy type and see if we can find something else first
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     // ### impl specifically - only works b/c vector isn't used in impl ###
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 }

akonadi

Skip menu "akonadi"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal