00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "itemserializer_p.h"
00022 #include "item.h"
00023 #include "itemserializerplugin.h"
00024 #include "attributefactory.h"
00025
00026
00027 #include <kdebug.h>
00028 #include <kmimetype.h>
00029 #include <kglobal.h>
00030
00031
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
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
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
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
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
00174 if ( matchingIndexes.isEmpty() )
00175 return mDefaultPlugin;
00176
00177 if ( matchingIndexes.size() == 1 )
00178 return allPlugins[matchingIndexes.first()];
00179
00180
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
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
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
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
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
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
00315 ItemSerializerPlugin* ItemSerializer::pluginForMimeType( const QString & mimetype )
00316 {
00317
00318 if ( s_pluginRegistry->cachedPlugins.contains( mimetype ) )
00319 return s_pluginRegistry->cachedPlugins.value( mimetype );
00320
00321 ItemSerializerPlugin *plugin = 0;
00322
00323
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
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 }