akonadi
item.cpp
00001 /* 00002 Copyright (c) 2006 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 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 } // anon namespace 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 // the bind() doesn't throw, so if shared_ptr 00078 // construction line below, or anything else after it, 00079 // throws, we're unlocked. Mark us as such: 00080 locked = false; 00081 const shared_ptr<T> result( t, bind( &QReadWriteLock::unlock, rwl ) ); 00082 // from now on, the shared_ptr is responsible for unlocking 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 // Change to something != RFC822 as soon as the server supports it 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; // clones 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 // 0. Nothing there - nothing to convert from, either 00339 if ( d->mPayloads.empty() ) 00340 return false; 00341 00342 // 1. Look whether we already have one: 00343 if ( d->hasMetaTypeId( mtid ) ) 00344 return true; 00345 00346 // recursion detection (shouldn't trigger, but does if the 00347 // serialiser plugins are acting funky): 00348 if ( d->mConversionInProgress ) 00349 return false; 00350 00351 // 2. Try to create one by conversion from a different representation: 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 // Stable Insertion Sort - N is typically _very_ low (1 or 2). 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 )
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:18 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:18 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.