1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtContacts module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 or version 3 as published by the Free |
20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
22 | ** following information to ensure the GNU Lesser General Public License |
23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
25 | ** |
26 | ** As a special exception, The Qt Company gives you certain additional |
27 | ** rights. These rights are described in The Qt Company LGPL Exception |
28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
29 | ** |
30 | ** $QT_END_LICENSE$ |
31 | ** |
32 | ****************************************************************************/ |
33 | |
34 | #ifndef QCONTACTDETAIL_P_H |
35 | #define QCONTACTDETAIL_P_H |
36 | |
37 | // |
38 | // W A R N I N G |
39 | // ------------- |
40 | // |
41 | // This file is not part of the Qt API. It exists purely as an |
42 | // implementation detail. This header file may change from version to |
43 | // version without notice, or even be removed. |
44 | // |
45 | // We mean it. |
46 | // |
47 | |
48 | #include <QMap> |
49 | #include <QList> |
50 | #include <QString> |
51 | #include <QVariant> |
52 | #include <QDateTime> |
53 | #include <QAtomicInt> |
54 | #include <QSharedData> |
55 | #include <QStringList> |
56 | |
57 | #include "qcontactdetail.h" |
58 | |
59 | QT_BEGIN_NAMESPACE_CONTACTS |
60 | |
61 | class QContactDetailPrivate : public QSharedData |
62 | { |
63 | public: |
64 | static QAtomicInt lastDetailKey; |
65 | |
66 | // all details have these fields |
67 | QList<int> m_contexts; |
68 | |
69 | // detail metadata |
70 | QString m_provenance; |
71 | QContactDetail::DetailType m_type; |
72 | QContactDetail::AccessConstraints m_access; |
73 | int m_detailId; |
74 | int m_hasValueBitfield; // subclass types must set the hasValue bit for any field value which isn't stored in m_extraData. |
75 | |
76 | // extra field data |
77 | QMap<int, QVariant> ; |
78 | |
79 | QContactDetailPrivate() |
80 | : m_type(QContactDetail::TypeUndefined) |
81 | , m_access(QContactDetail::NoConstraint) |
82 | , m_detailId(lastDetailKey.fetchAndAddOrdered(valueToAdd: 1)) |
83 | , m_hasValueBitfield(0) {} |
84 | QContactDetailPrivate(QContactDetail::DetailType detailType) |
85 | : m_type(detailType) |
86 | , m_access(QContactDetail::NoConstraint) |
87 | , m_detailId(lastDetailKey.fetchAndAddOrdered(valueToAdd: 1)) |
88 | , m_hasValueBitfield(0) {} |
89 | QContactDetailPrivate(const QContactDetailPrivate& other) |
90 | : QSharedData(other) |
91 | , m_contexts(other.m_contexts) |
92 | , m_provenance(other.m_provenance) |
93 | , m_type(other.m_type) |
94 | , m_access(other.m_access) |
95 | , m_detailId(other.m_detailId) |
96 | , m_hasValueBitfield(other.m_hasValueBitfield) |
97 | , m_extraData(other.m_extraData) {} |
98 | virtual ~QContactDetailPrivate() {} |
99 | |
100 | virtual QContactDetailPrivate *clone() { |
101 | // NOTE: this one must be called for extension (non-built-in) types ONLY |
102 | // otherwise slicing will occur on detach, leading to crashes! |
103 | Q_ASSERT(this->m_type == QContactDetail::TypeUndefined || this->m_type > QContactDetail::TypeVersion); // TypeVersion is the builtin type with largest type value. |
104 | return new QContactDetailPrivate(*this); |
105 | } |
106 | |
107 | virtual bool operator==(const QContactDetailPrivate& other) const { // doesn't check detailId or provenance |
108 | if (m_type != other.m_type |
109 | || !bitfieldsEqual(first: m_hasValueBitfield, second: other.m_hasValueBitfield, ignore: FieldProvenanceBit) |
110 | || m_contexts != other.m_contexts |
111 | || m_access != other.m_access |
112 | || m_extraData.size() != other.m_extraData.size()) { |
113 | return false; |
114 | } |
115 | |
116 | const QMap<int, QVariant> &thisValues(values()); |
117 | const QMap<int, QVariant> &otherValues(other.values()); |
118 | int thisSize = thisValues.contains(akey: QContactDetail::FieldProvenance) ? thisValues.size() - 1 : thisValues.size(); |
119 | int otherSize = otherValues.contains(akey: QContactDetail::FieldProvenance) ? otherValues.size() - 1 : otherValues.size(); |
120 | if (thisSize != otherSize) |
121 | return false; |
122 | |
123 | QMap<int, QVariant>::const_iterator it = thisValues.constBegin(), end = thisValues.constEnd(); |
124 | QMap<int, QVariant>::const_iterator otherIt; |
125 | for ( ; it != end; ++it) { |
126 | if (it.key() == QContactDetail::FieldProvenance) |
127 | continue; |
128 | otherIt = otherValues.constFind(akey: it.key()); |
129 | if (otherIt == otherValues.constEnd()) |
130 | return false; |
131 | if (it.value().canConvert<QList<int> >()) { |
132 | // QList<int> values must be compared as QList<int> not as QVariant<QList<int> >... |
133 | if (it.value().value<QList<int> >() != otherIt.value().value<QList<int> >()) |
134 | return false; |
135 | } else if (it.value() != otherIt.value()) { |
136 | // Everything else can be compared directly by value. |
137 | return false; |
138 | } |
139 | } |
140 | return true; |
141 | } |
142 | bool operator!=(const QContactDetailPrivate& other) const {return !(other == *this);} |
143 | |
144 | static void setAccessConstraints(QContactDetail *d, QContactDetail::AccessConstraints constraint) |
145 | { |
146 | d->d->m_access = constraint; |
147 | } |
148 | |
149 | static void setProvenance(QContactDetail *d, const QString &newProvenance) |
150 | { |
151 | d->d->m_provenance = newProvenance; |
152 | d->d->setHasValueBitfieldBit(value: !newProvenance.isEmpty(), whichBit: FieldProvenanceBit); |
153 | } |
154 | |
155 | static const QContactDetailPrivate* detailPrivate(const QContactDetail& detail) |
156 | { |
157 | return detail.d.constData(); |
158 | } |
159 | |
160 | // built-in fields hasValue bitfield bit |
161 | enum BuiltinFieldBitfieldBits { |
162 | FieldContextBit = 0, |
163 | FieldDetailUriBit, |
164 | FieldLinkedDetailUrisBit, |
165 | FieldProvenanceBit |
166 | }; |
167 | // custom types can define more (for fields which are a complicated view of Field>FieldMaximumUserVisible data) |
168 | inline bool hasValueBitfieldBitSet(unsigned int whichBit) const { |
169 | unsigned int mask = 1 << whichBit; |
170 | return m_hasValueBitfield & mask; |
171 | } |
172 | inline void setHasValueBitfieldBit(bool _value, unsigned int whichBit) { |
173 | unsigned int mask = 1 << whichBit; |
174 | if (_value) { |
175 | m_hasValueBitfield |= mask; // set |
176 | } else { |
177 | m_hasValueBitfield &= ~mask; // clear |
178 | } |
179 | } |
180 | static inline bool bitfieldsEqual(int first, int second, unsigned int ignore) { |
181 | unsigned int mask = 1 << ignore; |
182 | return (first & ~mask) == (second & ~mask); // clear the ignored bit in both |
183 | } |
184 | |
185 | virtual bool setValue(int field, const QVariant &_value) { |
186 | switch (field) { |
187 | case QContactDetail::FieldContext: { |
188 | if (_value.userType() == QMetaType::Int || _value.userType() == QMetaType::UInt) { |
189 | m_contexts = QList<int>() << _value.toInt(); |
190 | } else { |
191 | m_contexts = _value.value<QList<int> >(); |
192 | } |
193 | setHasValueBitfieldBit(value: true, whichBit: FieldContextBit); |
194 | return true; |
195 | } |
196 | case QContactDetail::FieldProvenance: { |
197 | m_provenance = _value.toString(); |
198 | setHasValueBitfieldBit(value: !m_provenance.isEmpty(), whichBit: FieldProvenanceBit); |
199 | return true; |
200 | } |
201 | default: { |
202 | // add the data as an extraData field |
203 | m_extraData.insert(akey: field, avalue: _value); |
204 | // don't need to set hasValueBitfield bit for fields stored in extra data. |
205 | return true; |
206 | } |
207 | } |
208 | } |
209 | |
210 | virtual bool removeValue(int field) { |
211 | switch (field) { |
212 | case QContactDetail::FieldContext: { |
213 | m_contexts = QList<int>(); |
214 | setHasValueBitfieldBit(value: false, whichBit: FieldContextBit); |
215 | return true; |
216 | } |
217 | case QContactDetail::FieldProvenance: { |
218 | m_provenance = QString(); |
219 | setHasValueBitfieldBit(value: false, whichBit: FieldProvenanceBit); |
220 | return true; |
221 | } |
222 | default: { |
223 | return m_extraData.remove(akey: field); |
224 | // don't need to clear hasValueBitfield bit for fields stored in extra data. |
225 | } |
226 | } |
227 | } |
228 | |
229 | virtual bool hasValue(int field) const { |
230 | switch (field) { |
231 | case QContactDetail::FieldContext: return hasValueBitfieldBitSet(whichBit: FieldContextBit); |
232 | case QContactDetail::FieldProvenance: return hasValueBitfieldBitSet(whichBit: FieldProvenanceBit); |
233 | default: return m_extraData.contains(akey: field); |
234 | } |
235 | } |
236 | |
237 | virtual bool isEmpty() const { |
238 | return m_hasValueBitfield == 0 && m_extraData.isEmpty(); |
239 | } |
240 | |
241 | virtual QMap<int, QVariant> values() const { |
242 | QMap<int, QVariant> retn; |
243 | if (hasValueBitfieldBitSet(whichBit: FieldContextBit)) { |
244 | retn.insert(akey: QContactDetail::FieldContext, avalue: QVariant::fromValue<QList<int> >(value: m_contexts)); |
245 | } |
246 | if (hasValueBitfieldBitSet(whichBit: FieldProvenanceBit)) { |
247 | retn.insert(akey: QContactDetail::FieldProvenance, avalue: QVariant::fromValue<QString>(value: m_provenance)); |
248 | } |
249 | QMap<int, QVariant>::const_iterator it = m_extraData.constBegin(), end = m_extraData.constEnd(); |
250 | for ( ; it != end; ++it) { |
251 | if (it.key() <= QContactDetail::FieldMaximumUserVisible) { |
252 | retn.insert(akey: it.key(), avalue: it.value()); |
253 | } |
254 | } |
255 | return retn; |
256 | } |
257 | |
258 | virtual QVariant value(int field) const { |
259 | switch (field) { |
260 | case QContactDetail::FieldContext: return QVariant::fromValue<QList<int> >(value: m_contexts); |
261 | case QContactDetail::FieldProvenance: return QVariant::fromValue<QString>(value: m_provenance); |
262 | default: return m_extraData.value(akey: field); |
263 | } |
264 | } |
265 | |
266 | static QContactDetailPrivate *construct(QContactDetail::DetailType detailType); |
267 | }; |
268 | |
269 | class QContactDetailBuiltinPrivateBase : public QContactDetailPrivate |
270 | { |
271 | public: |
272 | enum MemberType { |
273 | Bool, |
274 | Double, |
275 | Int, |
276 | IntList, |
277 | String, |
278 | StringList, |
279 | Date, |
280 | DateTime, |
281 | Url, |
282 | ByteArray, |
283 | Variant, |
284 | }; |
285 | |
286 | struct Member { |
287 | MemberType type; |
288 | size_t offset; |
289 | }; |
290 | |
291 | enum { BaseFieldOffset = 4 }; |
292 | |
293 | QContactDetailBuiltinPrivateBase(QContactDetail::DetailType type) |
294 | : QContactDetailPrivate(type) |
295 | { |
296 | } |
297 | QContactDetailBuiltinPrivateBase(const QContactDetailBuiltinPrivateBase& other) |
298 | : QContactDetailPrivate(other) |
299 | { |
300 | } |
301 | |
302 | template<typename Subclass> |
303 | static const void* memberAddress(const Subclass *base, size_t offset) |
304 | { |
305 | return reinterpret_cast<const void*>(reinterpret_cast<const char *>(base) + offset); |
306 | } |
307 | template<typename Subclass> |
308 | static void* memberAddress(Subclass *base, size_t offset) |
309 | { |
310 | return reinterpret_cast<void*>(reinterpret_cast<char *>(base) + offset); |
311 | } |
312 | |
313 | template<typename T, typename Subclass> |
314 | static const T* memberVariable(const Subclass *base, size_t offset) |
315 | { |
316 | return reinterpret_cast<const T*>(memberAddress(base, offset)); |
317 | } |
318 | template<typename T, typename Subclass> |
319 | static T* memberVariable(Subclass *base, size_t offset) |
320 | { |
321 | return reinterpret_cast<T*>(memberAddress(base, offset)); |
322 | } |
323 | |
324 | template<typename Subclass> |
325 | void reinitialize(Subclass* us, const Member& member) |
326 | { |
327 | switch (member.type) { |
328 | case Bool: *memberVariable<bool>(us, member.offset) = false; return; |
329 | case Double: *memberVariable<double>(us, member.offset) = 0.0; return; |
330 | case Int: *memberVariable<int>(us, member.offset) = 0; return; |
331 | case IntList: reinitialize<QList<int> >(us, member); return; |
332 | case String: reinitialize<QString>(us, member); return; |
333 | case StringList: reinitialize<QStringList>(us, member); return; |
334 | case Date: reinitialize<QDate>(us, member); return; |
335 | case DateTime: reinitialize<QDateTime>(us, member); return; |
336 | case Url: reinitialize<QUrl>(us, member); return; |
337 | case ByteArray: reinitialize<QByteArray>(us, member); return; |
338 | case Variant: reinitialize<QVariant>(us, member); return; |
339 | default: qFatal(msg: "Unsupported field type" ); |
340 | } |
341 | } |
342 | |
343 | template<typename Subclass> |
344 | static void setValueFromVariant(Subclass* us, const QVariant& value, const Member& member) |
345 | { |
346 | switch (member.type) { |
347 | case Bool: setValueFromVariant<bool>(us, value, member); return; |
348 | case Double: setValueFromVariant<double>(us, value, member); return; |
349 | case Int: setValueFromVariant<int>(us, value, member); return; |
350 | case IntList: setValueFromVariant<QList<int> >(us, value, member); return; |
351 | case String: setValueFromVariant<QString>(us, value, member); return; |
352 | case StringList: setValueFromVariant<QStringList>(us, value, member); return; |
353 | case Date: *memberVariable<QDate>(us, member.offset) = value.userType() == QMetaType::QDateTime ? value.value<QDateTime>().date() : value.value<QDate>(); return; |
354 | case DateTime: setValueFromVariant<QDateTime>(us, value, member); return; |
355 | case Url: setValueFromVariant<QUrl>(us, value, member); return; |
356 | case ByteArray: *memberVariable<QByteArray>(us, member.offset) = value.userType() == QMetaType::QString ? value.value<QString>().toUtf8() : value.value<QByteArray>(); return; |
357 | case Variant: *memberVariable<QVariant>(us, member.offset) = value; return; |
358 | default: qFatal(msg: "Unsupported field type" ); |
359 | } |
360 | } |
361 | |
362 | template<typename Subclass> |
363 | static bool equal(const Subclass* us, const Subclass* them, const Member& member) |
364 | { |
365 | switch (member.type) { |
366 | case Bool: return equal<bool>(us, them, member); |
367 | case Double: return equal<double>(us, them, member); |
368 | case Int: return equal<int>(us, them, member); |
369 | case IntList: return equal<QList<int> >(us, them, member); |
370 | case String: return equal<QString>(us, them, member); |
371 | case StringList: return equal<QStringList>(us, them, member); |
372 | case Date: return equal<QDate>(us, them, member); |
373 | case DateTime: return equal<QDateTime>(us, them, member); |
374 | case Url: return equal<QUrl>(us, them, member); |
375 | case ByteArray: return equal<QByteArray>(us, them, member); |
376 | case Variant: return equal<QVariant>(us, them, member); |
377 | default: qFatal(msg: "Unsupported field type" ); |
378 | } |
379 | } |
380 | |
381 | template<typename Subclass> |
382 | static QVariant toVariant(const Subclass* us, const Member& member) |
383 | { |
384 | switch (member.type) { |
385 | case Bool: return QVariant(*memberVariable<bool>(us, member.offset)); |
386 | case Double: return QVariant(*memberVariable<double>(us, member.offset)); |
387 | case Int: return QVariant(*memberVariable<int>(us, member.offset)); |
388 | case IntList: return QVariant::fromValue(*memberVariable<QList<int> >(us, member.offset)); |
389 | case String: return QVariant(*memberVariable<QString>(us, member.offset)); |
390 | case StringList: return QVariant::fromValue(*memberVariable<QStringList>(us, member.offset)); |
391 | case Date: return QVariant(*memberVariable<QDate>(us, member.offset)); |
392 | case DateTime: { // if the value was likely set as a QDate, then return it as a QDate. |
393 | const QDateTime &dt(*memberVariable<QDateTime>(us, member.offset)); |
394 | return (dt.timeSpec() == Qt::LocalTime && dt.time() == QTime(0,0,0,0)) |
395 | ? QVariant(dt.date()) |
396 | : QVariant(dt); |
397 | } |
398 | case Url: return QVariant(*memberVariable<QUrl>(us, member.offset)); |
399 | case ByteArray: return QVariant(*memberVariable<QByteArray>(us, member.offset)); |
400 | case Variant: return *memberVariable<QVariant>(us, member.offset); |
401 | default: qFatal(msg: "Unsupported field type" ); |
402 | } |
403 | } |
404 | |
405 | private: |
406 | template<typename T, typename Subclass> |
407 | void reinitialize(Subclass* us, const Member& member) |
408 | { |
409 | *memberVariable<T>(us, member.offset) = T(); |
410 | } |
411 | |
412 | template<typename T, typename Subclass> |
413 | static void setValueFromVariant(Subclass* us, const QVariant& value, const Member& member) |
414 | { |
415 | *memberVariable<T>(us, member.offset) = value.value<T>(); |
416 | } |
417 | |
418 | template<typename T, typename Subclass> |
419 | static bool equal(const Subclass* us, const Subclass* them, const Member& member) |
420 | { |
421 | return *memberVariable<T>(us, member.offset) == *memberVariable<int>(them, member.offset); |
422 | } |
423 | }; |
424 | |
425 | template <typename Subclass> |
426 | class QContactDetailBuiltinPrivate : public QContactDetailBuiltinPrivateBase |
427 | { |
428 | public: |
429 | static const Member s_members[]; |
430 | |
431 | QContactDetailBuiltinPrivate(QContactDetail::DetailType type) |
432 | : QContactDetailBuiltinPrivateBase(type) |
433 | { |
434 | } |
435 | |
436 | const Subclass *subclass() const |
437 | { |
438 | return static_cast<const Subclass *>(this); |
439 | } |
440 | Subclass *subclass() |
441 | { |
442 | return static_cast<Subclass *>(this); |
443 | } |
444 | |
445 | QContactDetailPrivate *clone() { |
446 | return new Subclass(*subclass()); |
447 | } |
448 | |
449 | bool operator==(const QContactDetailBuiltinPrivate& other) const |
450 | { |
451 | Subclass *us(subclass()); |
452 | const Subclass *them(other.subclass()); |
453 | for (int i = 0; i < Subclass::FieldCount; ++i) { |
454 | if (!equal(us, them, s_members[i])) { |
455 | return false; |
456 | } |
457 | } |
458 | return QContactDetailPrivate::operator==(other); |
459 | } |
460 | |
461 | template<typename T> |
462 | const T& memberValue(int field) const |
463 | { |
464 | return *memberVariable<T>(subclass(), s_members[field].offset); |
465 | } |
466 | |
467 | template<typename T> |
468 | void setMemberValue(int field, const T& value) |
469 | { |
470 | *memberVariable<T>(subclass(), s_members[field].offset) = value; |
471 | setHasValueBitfieldBit(value: true, whichBit: field + BaseFieldOffset); |
472 | } |
473 | |
474 | bool setValue(int field, const QVariant &value) |
475 | { |
476 | if (field < Subclass::FieldCount) { |
477 | setValueFromVariant(subclass(), value, s_members[field]); |
478 | setHasValueBitfieldBit(value: true, whichBit: field + BaseFieldOffset); |
479 | return true; |
480 | } |
481 | return QContactDetailPrivate::setValue(field, value: value); |
482 | } |
483 | |
484 | bool removeValue(int field) |
485 | { |
486 | if (field < Subclass::FieldCount) { |
487 | reinitialize(subclass(), s_members[field]); |
488 | setHasValueBitfieldBit(value: false, whichBit: field + BaseFieldOffset); |
489 | return true; |
490 | } |
491 | return QContactDetailPrivate::removeValue(field); |
492 | } |
493 | |
494 | bool hasValue(int field) const |
495 | { |
496 | if (field < Subclass::FieldCount) { |
497 | return hasValueBitfieldBitSet(whichBit: field + BaseFieldOffset); |
498 | } |
499 | return QContactDetailPrivate::hasValue(field); |
500 | } |
501 | |
502 | QMap<int, QVariant> values() const |
503 | { |
504 | QMap<int, QVariant> retn = QContactDetailPrivate::values(); |
505 | for (int i = 0; i < Subclass::FieldCount; ++i) { |
506 | if (hasValueBitfieldBitSet(whichBit: i + BaseFieldOffset)) { |
507 | retn.insert(i, value(field: i)); |
508 | } |
509 | } |
510 | return retn; |
511 | } |
512 | |
513 | QVariant value(int field) const |
514 | { |
515 | if (field < Subclass::FieldCount) { |
516 | return toVariant(subclass(), s_members[field]); |
517 | } |
518 | return QContactDetailPrivate::value(field); |
519 | } |
520 | }; |
521 | |
522 | QT_END_NAMESPACE_CONTACTS |
523 | |
524 | QT_BEGIN_NAMESPACE |
525 | // allow shared data / detach for specific detail types |
526 | template <> inline QTCONTACTS_PREPEND_NAMESPACE(QContactDetailPrivate) *QSharedDataPointer<QTCONTACTS_PREPEND_NAMESPACE(QContactDetailPrivate)>::clone() |
527 | { |
528 | return d->clone(); |
529 | } |
530 | QT_END_NAMESPACE |
531 | |
532 | #endif // QCONTACTDETAIL_P_H |
533 | |