• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

akonadi

itemserializer.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 "itemserializer_p.h"
00022 #include "item.h"
00023 #include "itemserializerplugin.h"
00024 #include "attributefactory.h"
00025 
00026 // KDE core
00027 #include <kdebug.h>
00028 #include <kmimetype.h>
00029 #include <kglobal.h>
00030 
00031 // Qt
00032 #include <QtCore/QBuffer>
00033 #include <QtCore/QFile>
00034 #include <QtCore/QIODevice>
00035 #include <QtCore/QHash>
00036 #include <QtCore/QString>
00037 #include <QtCore/QStringList>
00038 
00039 #include <boost/graph/adjacency_list.hpp>
00040 #include <boost/graph/topological_sort.hpp>
00041 
00042 // temporary
00043 #include "pluginloader_p.h"
00044 
00045 
00046 
00047 namespace Akonadi {
00048 
00049 class DefaultItemSerializerPlugin;
00050 
00051 class DefaultItemSerializerPlugin : public ItemSerializerPlugin
00052 {
00053 public:
00054     DefaultItemSerializerPlugin() { }
00055 
00056     bool deserialize( Item& item, const QByteArray& label, QIODevice& data, int )
00057     {
00058         if ( label != Item::FullPayload )
00059           return false;
00060         item.setPayload( data.readAll() );
00061         return true;
00062     }
00063 
00064     void serialize( const Item& item, const QByteArray& label, QIODevice& data, int& )
00065     {
00066         Q_ASSERT( label == Item::FullPayload );
00067         Q_UNUSED( label );
00068         if ( item.hasPayload<QByteArray>() )
00069             data.write( item.payload<QByteArray>() );
00070     }
00071 
00072 };
00073 
00074 K_GLOBAL_STATIC( DefaultItemSerializerPlugin, s_defaultItemSerializerPlugin )
00075 
00076 class PluginEntry
00077 {
00078   public:
00079     PluginEntry()
00080       : mPlugin( 0 )
00081     {
00082     }
00083 
00084     explicit PluginEntry( const QString &identifier, ItemSerializerPlugin *plugin = 0 )
00085       : mIdentifier( identifier ), mPlugin( plugin )
00086     {
00087     }
00088 
00089     inline ItemSerializerPlugin* plugin() const
00090     {
00091       if ( mPlugin )
00092         return mPlugin;
00093 
00094       QObject *object = PluginLoader::self()->createForName( mIdentifier );
00095       if ( !object ) {
00096         kWarning() << "ItemSerializerPluginLoader: "
00097                    << "plugin" << mIdentifier << "is not valid!" << endl;
00098 
00099         // we try to use the default in that case
00100         mPlugin = s_defaultItemSerializerPlugin;
00101       }
00102 
00103       mPlugin = qobject_cast<ItemSerializerPlugin*>( object );
00104       if ( !mPlugin ) {
00105         kWarning() << "ItemSerializerPluginLoader: "
00106                    << "plugin" << mIdentifier << "doesn't provide interface ItemSerializerPlugin!" << endl;
00107 
00108         // we try to use the default in that case
00109         mPlugin = s_defaultItemSerializerPlugin;
00110       }
00111 
00112       Q_ASSERT( mPlugin );
00113 
00114       return mPlugin;
00115     }
00116 
00117     QString type() const { return mIdentifier; }
00118 
00119     bool operator<( const PluginEntry &other ) const
00120     {
00121       return mIdentifier < other.mIdentifier;
00122     }
00123     bool operator<( const QString &type ) const
00124     {
00125       return mIdentifier < type;
00126     }
00127 
00128   private:
00129     QString mIdentifier;
00130     mutable ItemSerializerPlugin *mPlugin;
00131 };
00132 
00133 static bool operator<( const QString &type, const PluginEntry &entry )
00134 {
00135   return type < entry.type();
00136 }
00137 
00138 
00139 class PluginRegistry
00140 {
00141   public:
00142     PluginRegistry() :
00143       mDefaultPlugin( PluginEntry( QLatin1String("application/octet-stream"), s_defaultItemSerializerPlugin ) )
00144     {
00145       const PluginLoader* pl = PluginLoader::self();
00146       if ( !pl ) {
00147         kWarning() << "Cannot instantiate plugin loader!" << endl;
00148         return;
00149       }
00150       const QStringList types = pl->types();
00151       kDebug() << "ItemSerializerPluginLoader: "
00152                << "found" << types.size() << "plugins." << endl;
00153       allPlugins.reserve( types.size() + 1 );
00154       foreach ( const QString &type, types )
00155         allPlugins.append( PluginEntry( type ) );
00156       allPlugins.append( mDefaultPlugin );
00157       std::sort( allPlugins.begin(), allPlugins.end() );
00158     }
00159 
00160     const PluginEntry& findBestMatch( const QString &type )
00161     {
00162       KMimeType::Ptr mimeType = KMimeType::mimeType( type, KMimeType::ResolveAliases );
00163       if ( mimeType.isNull() )
00164         return mDefaultPlugin;
00165 
00166       // step 1: find all plugins that match at all
00167       QVector<int> matchingIndexes;
00168       for ( int i = 0, end = allPlugins.size(); i < end; ++i ) {
00169         if ( mimeType->is( allPlugins[i].type() ) )
00170           matchingIndexes.append( i );
00171       }
00172 
00173       // 0 matches: no luck (shouldn't happend though, as application/octet-stream matches everything)
00174       if ( matchingIndexes.isEmpty() )
00175         return mDefaultPlugin;
00176       // 1 match: we are done
00177       if ( matchingIndexes.size() == 1 )
00178         return allPlugins[matchingIndexes.first()];
00179 
00180       // step 2: if we have more than one match, find the most specific one using topological sort
00181       boost::adjacency_list<> graph( matchingIndexes.size() );
00182       for ( int i = 0, end = matchingIndexes.size() ; i != end ; ++i ) {
00183         KMimeType::Ptr mimeType = KMimeType::mimeType( allPlugins[matchingIndexes[i]].type(), KMimeType::ResolveAliases );
00184         if ( mimeType.isNull() )
00185           continue;
00186         for ( int j = 0; j != end; ++j ) {
00187           if ( i != j && mimeType->is( allPlugins[matchingIndexes[j]].type() ) )
00188             boost::add_edge( j, i, graph );
00189         }
00190       }
00191 
00192       QVector<int> order;
00193       order.reserve( allPlugins.size() );
00194       try {
00195         boost::topological_sort( graph, std::back_inserter( order ) );
00196       } catch ( boost::not_a_dag &e ) {
00197         kWarning() << "Mimetype tree is not a DAG!";
00198         return mDefaultPlugin;
00199       }
00200 
00201       return allPlugins[matchingIndexes[order.first()]];
00202     }
00203 
00204     QVector<PluginEntry> allPlugins;
00205     QHash<QString, ItemSerializerPlugin*> cachedPlugins;
00206 
00207   private:
00208     PluginEntry mDefaultPlugin;
00209 };
00210 
00211 K_GLOBAL_STATIC( PluginRegistry, s_pluginRegistry )
00212 
00213 
00214 /*static*/
00215 void ItemSerializer::deserialize( Item& item, const QByteArray& label, const QByteArray& data, int version, bool external )
00216 {
00217 
00218     if ( external ) {
00219       QFile file( QString::fromUtf8(data) );
00220       if ( file.open( QIODevice:: ReadOnly ) ) {
00221         deserialize( item, label, file, version );
00222         file.close();
00223       }
00224     } else {
00225       QBuffer buffer;
00226       buffer.setData( data );
00227       buffer.open( QIODevice::ReadOnly );
00228       buffer.seek( 0 );
00229       deserialize( item, label, buffer, version );
00230       buffer.close();
00231     }
00232 }
00233 
00234 /*static*/
00235 void ItemSerializer::deserialize( Item& item, const QByteArray& label, QIODevice& data, int version )
00236 {
00237   if ( !ItemSerializer::pluginForMimeType( item.mimeType() )->deserialize( item, label, data, version ) ) {
00238     kWarning() << "Unable to deserialize payload part:" << label;
00239     data.seek( 0 );
00240     kWarning() << "Payload data was: " << data.readAll();
00241   }
00242 }
00243 
00244 /*static*/
00245 void ItemSerializer::serialize( const Item& item, const QByteArray& label, QByteArray& data, int &version )
00246 {
00247     QBuffer buffer;
00248     buffer.setBuffer( &data );
00249     buffer.open( QIODevice::WriteOnly );
00250     buffer.seek( 0 );
00251     serialize( item, label, buffer, version );
00252     buffer.close();
00253 }
00254 
00255 /*static*/
00256 void ItemSerializer::serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version )
00257 {
00258   if ( !item.hasPayload() )
00259     return;
00260   ItemSerializerPlugin *plugin = pluginForMimeType( item.mimeType() );
00261   plugin->serialize( item, label, data, version );
00262 }
00263 
00264 void ItemSerializer::apply( Item &item, const Item &other )
00265 {
00266   if ( !other.hasPayload() )
00267     return;
00268 
00269   ItemSerializerPlugin *plugin = pluginForMimeType( item.mimeType() );
00270 
00271   ItemSerializerPluginV2 *pluginV2 = dynamic_cast<ItemSerializerPluginV2*>( plugin );
00272   if ( pluginV2 ) {
00273     pluginV2->apply( item, other );
00274     return;
00275   }
00276 
00277   // Old-school merge:
00278   foreach ( const QByteArray &part, other.loadedPayloadParts() ) {
00279     QByteArray partData;
00280     QBuffer buffer;
00281     buffer.setBuffer( &partData );
00282     buffer.open( QIODevice::ReadWrite );
00283     buffer.seek( 0 );
00284     int version;
00285     serialize( other, part, buffer, version );
00286     buffer.seek( 0 );
00287     deserialize( item, part, buffer, version );
00288   }
00289 }
00290 
00291 QSet<QByteArray> ItemSerializer::parts( const Item & item )
00292 {
00293   if ( !item.hasPayload() )
00294     return QSet<QByteArray>();
00295   return pluginForMimeType( item.mimeType() )->parts( item );
00296 }
00297 
00298 QSet<QByteArray> ItemSerializer::availableParts( const Item & item )
00299 {
00300   if ( !item.hasPayload() )
00301     return QSet<QByteArray>();
00302   ItemSerializerPlugin *plugin = pluginForMimeType( item.mimeType() );
00303   ItemSerializerPluginV2 *pluginV2 = dynamic_cast<ItemSerializerPluginV2*>( plugin );
00304 
00305   if ( pluginV2 )
00306     return pluginV2->availableParts( item );
00307 
00308   if (item.hasPayload())
00309     return QSet<QByteArray>();
00310 
00311   return QSet<QByteArray>() << Item::FullPayload;
00312 }
00313 
00314 /*static*/
00315 ItemSerializerPlugin* ItemSerializer::pluginForMimeType( const QString & mimetype )
00316 {
00317   // plugin cached, so let's take that one
00318   if ( s_pluginRegistry->cachedPlugins.contains( mimetype ) )
00319     return s_pluginRegistry->cachedPlugins.value( mimetype );
00320 
00321   ItemSerializerPlugin *plugin = 0;
00322 
00323   // check if we have one that matches exactly
00324   const QVector<PluginEntry>::const_iterator it
00325     = qBinaryFind( s_pluginRegistry->allPlugins.constBegin(), s_pluginRegistry->allPlugins.constEnd(), mimetype );
00326   if ( it != s_pluginRegistry->allPlugins.constEnd() ) {
00327     plugin = ( *it ).plugin();
00328   } else {
00329     // check if we have a more generic plugin
00330     const PluginEntry &entry = s_pluginRegistry->findBestMatch( mimetype );
00331     kDebug() << "Did not find exactly matching serializer plugin for type" << mimetype
00332              << ", taking" << entry.type() << "as the closest match";
00333     plugin = entry.plugin();
00334   }
00335 
00336   Q_ASSERT(plugin);
00337   s_pluginRegistry->cachedPlugins.insert( mimetype, plugin );
00338   return plugin;
00339 }
00340 
00341 }

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
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
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