1/*
2 Copyright (c) 2008 Tobias Koenig <tokoe@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#ifndef AKONADI_ITEM_P_H
21#define AKONADI_ITEM_P_H
22
23#include <QtCore/QDateTime>
24#include <QtCore/QMap>
25#include <QtCore/QVarLengthArray>
26
27#include "entity_p.h"
28#include "itempayloadinternals_p.h"
29#include "tag.h"
30
31#include <boost/bind.hpp>
32
33#include <memory>
34#include <algorithm>
35#include <cassert>
36#include <vector>
37
38namespace Akonadi {
39
40namespace _detail {
41
42template <typename T>
43class clone_ptr {
44 T *t;
45public:
46 clone_ptr()
47 : t(0)
48 {
49 }
50 explicit clone_ptr(T *t)
51 : t(t)
52 {}
53 clone_ptr(const clone_ptr &other)
54 : t(other.t ? other.t->clone() : 0)
55 {}
56 ~clone_ptr()
57 {
58 delete t;
59 }
60 clone_ptr &operator=(const clone_ptr &other)
61 {
62 if (this != &other) {
63 clone_ptr copy(other);
64 swap(copy);
65 }
66 return *this;
67 }
68 void swap(clone_ptr &other)
69 {
70 using std::swap;
71 swap(t, other.t);
72 }
73 T *operator->() const
74 {
75 return get();
76 }
77 T &operator*() const
78 {
79 assert(get() != 0);
80 return *get();
81 }
82 T *get() const
83 {
84 return t;
85 }
86 T *release()
87 {
88 T *const r = t;
89 t = 0;
90 return r;
91 }
92 void reset(T *other = 0)
93 {
94 delete t;
95 t = other;
96 }
97
98private:
99 struct _save_bool
100 {
101 void f()
102 {
103 };
104 };
105 typedef void (_save_bool::*save_bool)();
106public:
107 operator save_bool() const
108 {
109 return get() ? &_save_bool::f : 0;
110 }
111};
112
113template <typename T>
114inline void swap(clone_ptr<T> &lhs, clone_ptr<T> &rhs)
115{
116 lhs.swap(rhs);
117}
118
119template <typename T, size_t N>
120class VarLengthArray {
121 QVarLengthArray<T, N> impl; // ###should be replaced by self-written container that doesn't waste so much space
122public:
123 typedef T value_type;
124 typedef T *iterator;
125 typedef const T *const_iterator;
126 typedef T *pointer;
127 typedef const T *const_pointer;
128 typedef T &reference;
129 typedef const T &const_reference;
130
131 explicit VarLengthArray(int size = 0)
132 : impl(size)
133 {
134 }
135 // compiler-generated dtor, copy ctor, copy assignment are ok
136 // swap() makes little sense
137
138 void push_back(const T &t)
139 {
140 impl.append(t);
141 }
142 int capacity() const
143 {
144 return impl.capacity();
145 }
146 void clear()
147 {
148 impl.clear();
149 }
150 size_t size() const
151 {
152 return impl.count();
153 }
154 bool empty() const
155 {
156 return impl.isEmpty();
157 }
158 void pop_back()
159 {
160 return impl.removeLast();
161 }
162 void reserve(size_t n)
163 {
164 impl.reserve(n);
165 }
166 void resize(size_t n)
167 {
168 impl.resize(n);
169 }
170
171 iterator begin()
172 {
173 return impl.data();
174 }
175 iterator end()
176 {
177 return impl.data() + impl.size();
178 }
179 const_iterator begin() const
180 {
181 return impl.data();
182 }
183 const_iterator end() const
184 {
185 return impl.data() + impl.size();
186 }
187 const_iterator cbegin() const
188 {
189 return begin();
190 }
191 const_iterator cend() const
192 {
193 return end();
194 }
195
196 reference front()
197 {
198 return *impl.data();
199 }
200 reference back()
201 {
202 return *(impl.data() + impl.size());
203 }
204 const_reference front() const
205 {
206 return *impl.data();
207 }
208 const_reference back() const
209 {
210 return *(impl.data() + impl.size());
211 }
212
213 reference operator[](size_t n)
214 {
215 return impl[n];
216 }
217 const_reference operator[](size_t n) const
218 {
219 return impl[n];
220 }
221};
222
223struct TypedPayload
224{
225 clone_ptr<PayloadBase> payload;
226 int sharedPointerId;
227 int metaTypeId;
228};
229
230struct BySharedPointerAndMetaTypeID : std::unary_function<TypedPayload, bool>
231{
232 const int spid;
233 const int mtid;
234 BySharedPointerAndMetaTypeID(int spid, int mtid)
235 : spid(spid)
236 , mtid(mtid)
237 {
238 }
239 bool operator()(const TypedPayload &tp) const
240 {
241 return (mtid == -1 || mtid == tp.metaTypeId)
242 && (spid == -1 || spid == tp.sharedPointerId) ;
243 }
244};
245
246}
247
248} // namespace Akonadi
249
250namespace std {
251template <>
252inline void swap<Akonadi::_detail::TypedPayload>(Akonadi::_detail::TypedPayload &lhs, Akonadi::_detail::TypedPayload &rhs)
253{
254 lhs.payload.swap(rhs.payload);
255 swap(lhs.sharedPointerId, rhs.sharedPointerId);
256 swap(lhs.metaTypeId, rhs.metaTypeId);
257}
258}
259
260namespace Akonadi {
261//typedef _detail::VarLengthArray<_detail::TypedPayload,2> PayloadContainer;
262typedef std::vector<_detail::TypedPayload> PayloadContainer;
263}
264
265// disable Q_FOREACH on PayloadContainer (b/c it likes to take copies and clone_ptr doesn't like that)
266template <>
267class QForeachContainer<Akonadi::PayloadContainer> {};
268
269namespace Akonadi {
270
271/**
272 * @internal
273 */
274class ItemPrivate : public EntityPrivate
275{
276public:
277 explicit ItemPrivate(Item::Id id = -1)
278 : EntityPrivate(id)
279 , mLegacyPayload()
280 , mPayloads()
281 , mConversionInProgress(false)
282 , mRevision(-1)
283 , mCollectionId(-1)
284 , mSize(0)
285 , mModificationTime()
286 , mFlagsOverwritten(false)
287 , mTagsOverwritten(false)
288 , mSizeChanged(false)
289 , mClearPayload(false)
290 {
291 }
292
293#if 0
294 ItemPrivate(const ItemPrivate &other)
295 : EntityPrivate(other)
296 {
297 mFlags = other.mFlags;
298 mRevision = other.mRevision;
299 mSize = other.mSize;
300 mModificationTime = other.mModificationTime;
301 mMimeType = other.mMimeType;
302 mLegacyPayload = other.mLegacyPayload;
303 mPayloads = other.mPayloads;
304 mConversionInProgress = false;
305 mAddedFlags = other.mAddedFlags;
306 mDeletedFlags = other.mDeletedFlags;
307 mFlagsOverwritten = other.mFlagsOverwritten;
308 mSizeChanged = other.mSizeChanged;
309 mCollectionId = other.mCollectionId;
310 mClearPayload = other.mClearPayload;
311 }
312#endif
313
314 ~ItemPrivate()
315 {
316 }
317
318 void resetChangeLog()
319 {
320 mFlagsOverwritten = false;
321 mAddedFlags.clear();
322 mDeletedFlags.clear();
323 mSizeChanged = false;
324 mTagsOverwritten = false;
325 mAddedTags.clear();
326 mDeletedTags.clear();
327 }
328
329 EntityPrivate *clone() const
330 {
331 return new ItemPrivate(*this);
332 }
333
334 bool hasMetaTypeId(int mtid) const
335 {
336 return std::find_if(mPayloads.begin(), mPayloads.end(),
337 _detail::BySharedPointerAndMetaTypeID(-1, mtid))
338 != mPayloads.end();
339 }
340
341 PayloadBase *payloadBaseImpl(int spid, int mtid) const
342 {
343 const PayloadContainer::const_iterator it
344 = std::find_if(mPayloads.begin(), mPayloads.end(),
345 _detail::BySharedPointerAndMetaTypeID(spid, mtid));
346 return it == mPayloads.end() ? 0 : it->payload.get() ;
347 }
348
349 bool movePayloadFrom(ItemPrivate *other, int mtid) const /*sic!*/
350 {
351 assert(other);
352 const size_t oldSize = mPayloads.size();
353 PayloadContainer &oPayloads = other->mPayloads;
354 const _detail::BySharedPointerAndMetaTypeID matcher(-1, mtid);
355 const size_t numMatching = std::count_if(oPayloads.begin(), oPayloads.end(), matcher);
356 mPayloads.resize(oldSize + numMatching);
357 using namespace std; // for swap()
358 for (PayloadContainer::iterator
359 dst = mPayloads.begin() + oldSize,
360 src = oPayloads.begin(), end = oPayloads.end() ; src != end ; ++src) {
361 if (matcher(*src)) {
362 swap(*dst, *src);
363 ++dst;
364 }
365 }
366 return numMatching > 0 ;
367 }
368
369#if 0
370 std::auto_ptr<PayloadBase> takePayloadBaseImpl(int spid, int mtid)
371 {
372 PayloadContainer::iterator it
373 = std::find_if(mPayloads.begin(), mPayloads.end(),
374 _detail::BySharedPointerAndMetaTypeID(spid, mtid));
375 if (it == mPayloads.end()) {
376 return std::auto_ptr<PayloadBase>();
377 }
378 std::rotate(it, it + 1, mPayloads.end());
379 std::auto_ptr<PayloadBase> result(it->payload.release());
380 mPayloads.pop_back();
381 return result;
382 }
383#endif
384
385 void setPayloadBaseImpl(int spid, int mtid, std::auto_ptr<PayloadBase> p, bool add) const /*sic!*/
386 {
387
388 if (!add) {
389 mLegacyPayload.reset();
390 }
391
392 if (!p.get()) {
393 if (!add) {
394 mPayloads.clear();
395 }
396 return;
397 }
398
399 // if !add, delete all payload variants
400 // (they're conversions of each other)
401 mPayloads.resize(add ? mPayloads.size() + 1 : 1);
402 _detail::TypedPayload &tp = mPayloads.back();
403 tp.payload.reset(p.release());
404 tp.sharedPointerId = spid;
405 tp.metaTypeId = mtid;
406 }
407
408 void setLegacyPayloadBaseImpl(std::auto_ptr<PayloadBase> p);
409 void tryEnsureLegacyPayload() const;
410
411 mutable _detail::clone_ptr<PayloadBase> mLegacyPayload;
412 mutable PayloadContainer mPayloads;
413 mutable bool mConversionInProgress;
414 int mRevision;
415 Item::Flags mFlags;
416 Tag::List mTags;
417 Entity::Id mCollectionId;
418 Collection::List mVirtualReferences;
419 qint64 mSize;
420 QDateTime mModificationTime;
421 QString mMimeType;
422 QString mGid;
423 Item::Flags mAddedFlags;
424 Item::Flags mDeletedFlags;
425 Tag::List mAddedTags;
426 Tag::List mDeletedTags;
427 QSet<QByteArray> mCachedPayloadParts;
428 bool mFlagsOverwritten : 1;
429 bool mTagsOverwritten : 1;
430 bool mSizeChanged : 1;
431 bool mClearPayload : 1;
432};
433
434}
435
436#endif
437