00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "item.h"
00021 #include "item_p.h"
00022 #include "itemserializer_p.h"
00023 #include "protocol_p.h"
00024
00025 #include <kurl.h>
00026 #include <kdebug.h>
00027
00028 #include <QtCore/QStringList>
00029 #include <QtCore/QReadWriteLock>
00030
00031 #include <algorithm>
00032 #include <map>
00033 #include <utility>
00034
00035 using namespace Akonadi;
00036 using namespace boost;
00037
00038 namespace {
00039
00040 struct nodelete {
00041 template <typename T>
00042 void operator()( T * ) {}
00043 };
00044
00045 struct ByTypeId {
00046 typedef bool result_type;
00047 bool operator()( const boost::shared_ptr<PayloadBase> & lhs, const boost::shared_ptr<PayloadBase> & rhs ) const {
00048 return strcmp( lhs->typeName(), rhs->typeName() ) < 0 ;
00049 }
00050 };
00051
00052 }
00053
00054 typedef QHash< QString, std::map< boost::shared_ptr<PayloadBase>, std::pair<int,int>, ByTypeId > > LegacyMap;
00055 Q_GLOBAL_STATIC( LegacyMap, typeInfoToMetaTypeIdMap )
00056 Q_GLOBAL_STATIC_WITH_ARGS( QReadWriteLock, legacyMapLock, ( QReadWriteLock::Recursive ) )
00057
00058 void Item::addToLegacyMappingImpl( const QString & mimeType, int spid, int mtid, std::auto_ptr<PayloadBase> p ) {
00059 if ( !p.get() )
00060 return;
00061 const boost::shared_ptr<PayloadBase> sp( p );
00062 const QWriteLocker locker( legacyMapLock() );
00063 std::pair<int,int> & item = (*typeInfoToMetaTypeIdMap())[mimeType][sp];
00064 item.first = spid;
00065 item.second = mtid;
00066 }
00067
00068 namespace {
00069 class MyReadLocker {
00070 public:
00071 explicit MyReadLocker( QReadWriteLock * rwl ) : rwl( rwl ), locked( false ) { if ( rwl ) rwl->lockForRead(); locked = true; }
00072 ~MyReadLocker() { if ( rwl && locked ) rwl->unlock(); }
00073
00074 template <typename T>
00075 shared_ptr<T> makeUnlockingPointer( T * t ) {
00076 if ( t ) {
00077
00078
00079
00080 locked = false;
00081 const shared_ptr<T> result( t, bind( &QReadWriteLock::unlock, rwl ) );
00082
00083 return result;
00084 } else {
00085 return shared_ptr<T>();
00086 }
00087 }
00088 private:
00089 QReadWriteLock * const rwl;
00090 bool locked;
00091 };
00092 }
00093
00094 static shared_ptr<const std::pair<int,int> > lookupLegacyMapping( const QString & mimeType, PayloadBase * p ) {
00095 MyReadLocker locker( legacyMapLock() );
00096 const LegacyMap::const_iterator hit = typeInfoToMetaTypeIdMap()->constFind( mimeType );
00097 if ( hit == typeInfoToMetaTypeIdMap()->constEnd() )
00098 return shared_ptr<const std::pair<int,int> >();
00099 const boost::shared_ptr<PayloadBase> sp( p, nodelete() );
00100 const LegacyMap::mapped_type::const_iterator it = hit->find( sp );
00101 if ( it == hit->end() )
00102 return shared_ptr<const std::pair<int,int> >();
00103
00104 return locker.makeUnlockingPointer( &it->second );
00105 }
00106
00107
00108 const char* Item::FullPayload = "RFC822";
00109
00110 Item::Item()
00111 : Entity( new ItemPrivate )
00112 {
00113 }
00114
00115 Item::Item( Id id )
00116 : Entity( new ItemPrivate( id ) )
00117 {
00118 }
00119
00120 Item::Item( const QString & mimeType )
00121 : Entity( new ItemPrivate )
00122 {
00123 d_func()->mMimeType = mimeType;
00124 }
00125
00126 Item::Item( const Item &other )
00127 : Entity( other )
00128 {
00129 }
00130
00131 Item::~Item()
00132 {
00133 }
00134
00135 Item::Flags Item::flags() const
00136 {
00137 return d_func()->mFlags;
00138 }
00139
00140 void Item::setFlag( const QByteArray & name )
00141 {
00142 Q_D( Item );
00143 d->mFlags.insert( name );
00144 if ( !d->mFlagsOverwritten ) {
00145 if ( d->mDeletedFlags.contains( name ) )
00146 d->mDeletedFlags.remove( name );
00147 else
00148 d->mAddedFlags.insert( name );
00149 }
00150 }
00151
00152 void Item::clearFlag( const QByteArray & name )
00153 {
00154 Q_D( Item );
00155 d->mFlags.remove( name );
00156 if ( !d->mFlagsOverwritten ) {
00157 if ( d->mAddedFlags.contains( name ) )
00158 d->mAddedFlags.remove( name );
00159 else
00160 d->mDeletedFlags.insert( name );
00161 }
00162 }
00163
00164 void Item::setFlags( const Flags &flags )
00165 {
00166 Q_D( Item );
00167 d->mFlags = flags;
00168 d->mFlagsOverwritten = true;
00169 }
00170
00171 void Item::clearFlags()
00172 {
00173 Q_D( Item );
00174 d->mFlags.clear();
00175 d->mFlagsOverwritten = true;
00176 }
00177
00178 QDateTime Item::modificationTime() const
00179 {
00180 return d_func()->mModificationTime;
00181 }
00182
00183 void Item::setModificationTime( const QDateTime &datetime )
00184 {
00185 d_func()->mModificationTime = datetime;
00186 }
00187
00188 bool Item::hasFlag( const QByteArray & name ) const
00189 {
00190 return d_func()->mFlags.contains( name );
00191 }
00192
00193 QSet<QByteArray> Item::loadedPayloadParts() const
00194 {
00195 return ItemSerializer::parts( *this );
00196 }
00197
00198 QByteArray Item::payloadData() const
00199 {
00200 int version = 0;
00201 QByteArray data;
00202 ItemSerializer::serialize( *this, FullPayload, data, version );
00203 return data;
00204 }
00205
00206 void Item::setPayloadFromData( const QByteArray &data )
00207 {
00208 ItemSerializer::deserialize( *this, FullPayload, data, 0, false );
00209 }
00210
00211 void Item::clearPayload()
00212 {
00213 d_func()->mClearPayload = true;
00214 }
00215
00216 int Item::revision() const
00217 {
00218 return d_func()->mRevision;
00219 }
00220
00221 void Item::setRevision( int rev )
00222 {
00223 d_func()->mRevision = rev;
00224 }
00225
00226 Entity::Id Item::storageCollectionId() const
00227 {
00228 return d_func()->mCollectionId;
00229 }
00230
00231 void Item::setStorageCollectionId( Entity::Id collectionId )
00232 {
00233 d_func()->mCollectionId = collectionId;
00234 }
00235
00236 QString Item::mimeType() const
00237 {
00238 return d_func()->mMimeType;
00239 }
00240
00241 void Item::setSize( qint64 size )
00242 {
00243 Q_D( Item );
00244 d->mSize = size;
00245 d->mSizeChanged = true;
00246 }
00247
00248 qint64 Item::size() const
00249 {
00250 return d_func()->mSize;
00251 }
00252
00253 void Item::setMimeType( const QString & mimeType )
00254 {
00255 d_func()->mMimeType = mimeType;
00256 }
00257
00258 bool Item::hasPayload() const
00259 {
00260 return d_func()->hasMetaTypeId( -1 );
00261 }
00262
00263 KUrl Item::url( UrlType type ) const
00264 {
00265 KUrl url;
00266 url.setProtocol( QString::fromLatin1( "akonadi" ) );
00267 url.addQueryItem( QLatin1String( "item" ), QString::number( id() ) );
00268
00269 if ( type == UrlWithMimeType )
00270 url.addQueryItem( QLatin1String( "type" ), mimeType() );
00271
00272 return url;
00273 }
00274
00275 Item Item::fromUrl( const KUrl &url )
00276 {
00277 if ( url.protocol() != QLatin1String( "akonadi" ) )
00278 return Item();
00279
00280 const QString itemStr = url.queryItem( QLatin1String( "item" ) );
00281 bool ok = false;
00282 Item::Id itemId = itemStr.toLongLong( &ok );
00283 if ( !ok )
00284 return Item();
00285
00286 return Item( itemId );
00287 }
00288
00289 namespace {
00290 class Dummy {};
00291 }
00292
00293 Q_GLOBAL_STATIC( Payload<Dummy>, dummyPayload )
00294
00295 PayloadBase* Item::payloadBase() const
00296 {
00297 Q_D( const Item );
00298 d->tryEnsureLegacyPayload();
00299 if ( d->mLegacyPayload )
00300 return d->mLegacyPayload.get();
00301 else
00302 return dummyPayload();
00303 }
00304
00305 void ItemPrivate::tryEnsureLegacyPayload() const
00306 {
00307 if ( !mLegacyPayload )
00308 for ( PayloadContainer::const_iterator it = mPayloads.begin(), end = mPayloads.end() ; it != end ; ++it )
00309 if ( lookupLegacyMapping( mMimeType, it->payload.get() ) )
00310 mLegacyPayload = it->payload;
00311 }
00312
00313 PayloadBase* Item::payloadBaseV2( int spid, int mtid ) const
00314 {
00315 return d_func()->payloadBaseImpl( spid, mtid );
00316 }
00317
00318 namespace {
00319 class ConversionGuard {
00320 const bool old;
00321 bool & b;
00322 public:
00323 explicit ConversionGuard( bool & b )
00324 : old( b ), b( b )
00325 {
00326 b = true;
00327 }
00328 ~ConversionGuard() {
00329 b = old;
00330 }
00331 };
00332 }
00333
00334
00335 bool Item::ensureMetaTypeId( int mtid ) const
00336 {
00337 Q_D( const Item );
00338
00339 if ( d->mPayloads.empty() )
00340 return false;
00341
00342
00343 if ( d->hasMetaTypeId( mtid ) )
00344 return true;
00345
00346
00347
00348 if ( d->mConversionInProgress )
00349 return false;
00350
00351
00352 try {
00353 const ConversionGuard guard( d->mConversionInProgress );
00354 Item converted = ItemSerializer::convert( *this, mtid );
00355 return d->movePayloadFrom( converted.d_func(), mtid );
00356 } catch ( const std::exception & e ) {
00357 kDebug() << "conversion threw:" << e.what();
00358 return false;
00359 } catch ( ... ) {
00360 kDebug() << "conversion threw something not derived from std::exception: fix the program!";
00361 return false;
00362 }
00363 }
00364
00365 static QString format_type( int spid, int mtid ) {
00366 return QString::fromLatin1( "sp(%1)<%2>" )
00367 .arg( spid ).arg( QLatin1String( QMetaType::typeName( mtid ) ) );
00368 }
00369
00370 static QString format_types( const PayloadContainer & c ) {
00371 QStringList result;
00372 for ( PayloadContainer::const_iterator it = c.begin(), end = c.end() ; it != end ; ++it )
00373 result.push_back( format_type( it->sharedPointerId, it->metaTypeId ) );
00374 return result.join( QLatin1String(", ") );
00375 }
00376
00377 #if 0
00378 QString Item::payloadExceptionText( int spid, int mtid ) const
00379 {
00380 Q_D( const Item );
00381 if ( d->mPayloads.empty() )
00382 return QLatin1String( "No payload set" );
00383 else
00384 return QString::fromLatin1( "Wrong payload type (requested: %1; present: %2" )
00385 .arg( format_type( spid, mtid ), format_types( d->mPayloads ) );
00386 }
00387 #else
00388 void Item::throwPayloadException( int spid, int mtid ) const
00389 {
00390 Q_D( const Item );
00391 if ( d->mPayloads.empty() )
00392 throw PayloadException( "No payload set" );
00393 else
00394 throw PayloadException( QString::fromLatin1( "Wrong payload type (requested: %1; present: %2" )
00395 .arg( format_type( spid, mtid ), format_types( d->mPayloads ) ) );
00396 }
00397 #endif
00398
00399 void Item::setPayloadBase( PayloadBase* p )
00400 {
00401 d_func()->setLegacyPayloadBaseImpl( std::auto_ptr<PayloadBase>( p ) );
00402 }
00403
00404 void ItemPrivate::setLegacyPayloadBaseImpl( std::auto_ptr<PayloadBase> p )
00405 {
00406 if ( const shared_ptr<const std::pair<int,int> > pair = lookupLegacyMapping( mMimeType, p.get() ) ) {
00407 std::auto_ptr<PayloadBase> clone;
00408 if ( p.get() )
00409 clone.reset( p->clone() );
00410 setPayloadBaseImpl( pair->first, pair->second, p, false );
00411 mLegacyPayload.reset( clone.release() );
00412 } else {
00413 mPayloads.clear();
00414 mLegacyPayload.reset( p.release() );
00415 }
00416 }
00417
00418 void Item::setPayloadBaseV2( int spid, int mtid, std::auto_ptr<PayloadBase> p )
00419 {
00420 d_func()->setPayloadBaseImpl( spid, mtid, p, false );
00421 }
00422
00423 void Item::addPayloadBaseVariant( int spid, int mtid, std::auto_ptr<PayloadBase> p ) const
00424 {
00425 d_func()->setPayloadBaseImpl( spid, mtid, p, true );
00426 }
00427
00428 QSet<QByteArray> Item::availablePayloadParts() const
00429 {
00430 return ItemSerializer::availableParts( *this );
00431 }
00432
00433 QVector<int> Item::availablePayloadMetaTypeIds() const
00434 {
00435 QVector<int> result;
00436 Q_D( const Item );
00437 result.reserve( d->mPayloads.size() );
00438
00439 for ( PayloadContainer::const_iterator it = d->mPayloads.begin(), end = d->mPayloads.end() ; it != end ; ++it )
00440 result.insert( std::upper_bound( result.begin(), result.end(), it->metaTypeId ), it->metaTypeId );
00441 return result;
00442 }
00443
00444 void Item::apply( const Item &other )
00445 {
00446 if ( mimeType() != other.mimeType() || id() != other.id() ) {
00447 kDebug() << "mimeType() = " << mimeType() << "; other.mimeType() = " << other.mimeType();
00448 kDebug() << "id() = " << id() << "; other.id() = " << other.id();
00449 Q_ASSERT_X( false, "Item::apply", "mimetype or id missmatch" );
00450 }
00451
00452 setRemoteId( other.remoteId() );
00453 setRevision( other.revision() );
00454 setFlags( other.flags() );
00455 setModificationTime( other.modificationTime() );
00456 setSize( other.size() );
00457 setParentCollection( other.parentCollection() );
00458 setStorageCollectionId( other.storageCollectionId() );
00459 setRemoteId( other.remoteId() );
00460
00461 QList<QByteArray> attrs;
00462 foreach ( Attribute *attribute, other.attributes() )
00463 {
00464 addAttribute( attribute->clone() );
00465 attrs.append( attribute->type() );
00466 }
00467
00468 QMutableHashIterator<QByteArray, Attribute*> it( d_ptr->mAttributes );
00469 while ( it.hasNext() ) {
00470 it.next();
00471 if ( !attrs.contains( it.key() ) ) {
00472 delete it.value();
00473 it.remove();
00474 }
00475 }
00476
00477 ItemSerializer::apply( *this, other );
00478 d_func()->resetChangeLog();
00479 }
00480
00481 AKONADI_DEFINE_PRIVATE( Item )