/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDETAIL_P_H #define QCONTACTDETAIL_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include "qcontactdetail.h" QT_BEGIN_NAMESPACE_CONTACTS class QContactDetailPrivate : public QSharedData { public: static QAtomicInt lastDetailKey; // all details have these fields QList m_contexts; // detail metadata QString m_provenance; QContactDetail::DetailType m_type; QContactDetail::AccessConstraints m_access; int m_detailId; int m_hasValueBitfield; // subclass types must set the hasValue bit for any field value which isn't stored in m_extraData. // extra field data QMap m_extraData; QContactDetailPrivate() : m_type(QContactDetail::TypeUndefined) , m_access(QContactDetail::NoConstraint) , m_detailId(lastDetailKey.fetchAndAddOrdered(1)) , m_hasValueBitfield(0) {} QContactDetailPrivate(QContactDetail::DetailType detailType) : m_type(detailType) , m_access(QContactDetail::NoConstraint) , m_detailId(lastDetailKey.fetchAndAddOrdered(1)) , m_hasValueBitfield(0) {} QContactDetailPrivate(const QContactDetailPrivate& other) : QSharedData(other) , m_contexts(other.m_contexts) , m_provenance(other.m_provenance) , m_type(other.m_type) , m_access(other.m_access) , m_detailId(other.m_detailId) , m_hasValueBitfield(other.m_hasValueBitfield) , m_extraData(other.m_extraData) {} virtual ~QContactDetailPrivate() {} virtual QContactDetailPrivate *clone() { // NOTE: this one must be called for extension (non-built-in) types ONLY // otherwise slicing will occur on detach, leading to crashes! Q_ASSERT(this->m_type == QContactDetail::TypeUndefined || this->m_type > QContactDetail::TypeVersion); // TypeVersion is the builtin type with largest type value. return new QContactDetailPrivate(*this); } virtual bool operator==(const QContactDetailPrivate& other) const { // doesn't check detailId or provenance if (m_type != other.m_type || !bitfieldsEqual(m_hasValueBitfield, other.m_hasValueBitfield, FieldProvenanceBit) || m_contexts != other.m_contexts || m_access != other.m_access || m_extraData.size() != other.m_extraData.size()) { return false; } const QMap &thisValues(values()); const QMap &otherValues(other.values()); int thisSize = thisValues.contains(QContactDetail::FieldProvenance) ? thisValues.size() - 1 : thisValues.size(); int otherSize = otherValues.contains(QContactDetail::FieldProvenance) ? otherValues.size() - 1 : otherValues.size(); if (thisSize != otherSize) return false; QMap::const_iterator it = thisValues.constBegin(), end = thisValues.constEnd(); QMap::const_iterator otherIt; for ( ; it != end; ++it) { if (it.key() == QContactDetail::FieldProvenance) continue; otherIt = otherValues.constFind(it.key()); if (otherIt == otherValues.constEnd()) return false; if (it.value().canConvert >()) { // QList values must be compared as QList not as QVariant >... if (it.value().value >() != otherIt.value().value >()) return false; } else if (it.value() != otherIt.value()) { // Everything else can be compared directly by value. return false; } } return true; } bool operator!=(const QContactDetailPrivate& other) const {return !(other == *this);} static void setAccessConstraints(QContactDetail *d, QContactDetail::AccessConstraints constraint) { d->d->m_access = constraint; } static void setProvenance(QContactDetail *d, const QString &newProvenance) { d->d->m_provenance = newProvenance; d->d->setHasValueBitfieldBit(!newProvenance.isEmpty(), FieldProvenanceBit); } static const QContactDetailPrivate* detailPrivate(const QContactDetail& detail) { return detail.d.constData(); } // built-in fields hasValue bitfield bit enum BuiltinFieldBitfieldBits { FieldContextBit = 0, FieldDetailUriBit, FieldLinkedDetailUrisBit, FieldProvenanceBit }; // custom types can define more (for fields which are a complicated view of Field>FieldMaximumUserVisible data) inline bool hasValueBitfieldBitSet(unsigned int whichBit) const { unsigned int mask = 1 << whichBit; return m_hasValueBitfield & mask; } inline void setHasValueBitfieldBit(bool _value, unsigned int whichBit) { unsigned int mask = 1 << whichBit; if (_value) { m_hasValueBitfield |= mask; // set } else { m_hasValueBitfield &= ~mask; // clear } } static inline bool bitfieldsEqual(int first, int second, unsigned int ignore) { unsigned int mask = 1 << ignore; return (first & ~mask) == (second & ~mask); // clear the ignored bit in both } virtual bool setValue(int field, const QVariant &_value) { switch (field) { case QContactDetail::FieldContext: { if (_value.userType() == QMetaType::Int || _value.userType() == QMetaType::UInt) { m_contexts = QList() << _value.toInt(); } else { m_contexts = _value.value >(); } setHasValueBitfieldBit(true, FieldContextBit); return true; } case QContactDetail::FieldProvenance: { m_provenance = _value.toString(); setHasValueBitfieldBit(!m_provenance.isEmpty(), FieldProvenanceBit); return true; } default: { // add the data as an extraData field m_extraData.insert(field, _value); // don't need to set hasValueBitfield bit for fields stored in extra data. return true; } } } virtual bool removeValue(int field) { switch (field) { case QContactDetail::FieldContext: { m_contexts = QList(); setHasValueBitfieldBit(false, FieldContextBit); return true; } case QContactDetail::FieldProvenance: { m_provenance = QString(); setHasValueBitfieldBit(false, FieldProvenanceBit); return true; } default: { return m_extraData.remove(field); // don't need to clear hasValueBitfield bit for fields stored in extra data. } } } virtual bool hasValue(int field) const { switch (field) { case QContactDetail::FieldContext: return hasValueBitfieldBitSet(FieldContextBit); case QContactDetail::FieldProvenance: return hasValueBitfieldBitSet(FieldProvenanceBit); default: return m_extraData.contains(field); } } virtual bool isEmpty() const { return m_hasValueBitfield == 0 && m_extraData.isEmpty(); } virtual QMap values() const { QMap retn; if (hasValueBitfieldBitSet(FieldContextBit)) { retn.insert(QContactDetail::FieldContext, QVariant::fromValue >(m_contexts)); } if (hasValueBitfieldBitSet(FieldProvenanceBit)) { retn.insert(QContactDetail::FieldProvenance, QVariant::fromValue(m_provenance)); } QMap::const_iterator it = m_extraData.constBegin(), end = m_extraData.constEnd(); for ( ; it != end; ++it) { if (it.key() <= QContactDetail::FieldMaximumUserVisible) { retn.insert(it.key(), it.value()); } } return retn; } virtual QVariant value(int field) const { switch (field) { case QContactDetail::FieldContext: return QVariant::fromValue >(m_contexts); case QContactDetail::FieldProvenance: return QVariant::fromValue(m_provenance); default: return m_extraData.value(field); } } static QContactDetailPrivate *construct(QContactDetail::DetailType detailType); }; class QContactDetailBuiltinPrivateBase : public QContactDetailPrivate { public: enum MemberType { Bool, Double, Int, IntList, String, StringList, Date, DateTime, Url, ByteArray, Variant, }; struct Member { MemberType type; size_t offset; }; enum { BaseFieldOffset = 4 }; QContactDetailBuiltinPrivateBase(QContactDetail::DetailType type) : QContactDetailPrivate(type) { } QContactDetailBuiltinPrivateBase(const QContactDetailBuiltinPrivateBase& other) : QContactDetailPrivate(other) { } template static const void* memberAddress(const Subclass *base, size_t offset) { return reinterpret_cast(reinterpret_cast(base) + offset); } template static void* memberAddress(Subclass *base, size_t offset) { return reinterpret_cast(reinterpret_cast(base) + offset); } template static const T* memberVariable(const Subclass *base, size_t offset) { return reinterpret_cast(memberAddress(base, offset)); } template static T* memberVariable(Subclass *base, size_t offset) { return reinterpret_cast(memberAddress(base, offset)); } template void reinitialize(Subclass* us, const Member& member) { switch (member.type) { case Bool: *memberVariable(us, member.offset) = false; return; case Double: *memberVariable(us, member.offset) = 0.0; return; case Int: *memberVariable(us, member.offset) = 0; return; case IntList: reinitialize >(us, member); return; case String: reinitialize(us, member); return; case StringList: reinitialize(us, member); return; case Date: reinitialize(us, member); return; case DateTime: reinitialize(us, member); return; case Url: reinitialize(us, member); return; case ByteArray: reinitialize(us, member); return; case Variant: reinitialize(us, member); return; default: qFatal("Unsupported field type"); } } template static void setValueFromVariant(Subclass* us, const QVariant& value, const Member& member) { switch (member.type) { case Bool: setValueFromVariant(us, value, member); return; case Double: setValueFromVariant(us, value, member); return; case Int: setValueFromVariant(us, value, member); return; case IntList: setValueFromVariant >(us, value, member); return; case String: setValueFromVariant(us, value, member); return; case StringList: setValueFromVariant(us, value, member); return; case Date: *memberVariable(us, member.offset) = value.userType() == QMetaType::QDateTime ? value.value().date() : value.value(); return; case DateTime: setValueFromVariant(us, value, member); return; case Url: setValueFromVariant(us, value, member); return; case ByteArray: *memberVariable(us, member.offset) = value.userType() == QMetaType::QString ? value.value().toUtf8() : value.value(); return; case Variant: *memberVariable(us, member.offset) = value; return; default: qFatal("Unsupported field type"); } } template static bool equal(const Subclass* us, const Subclass* them, const Member& member) { switch (member.type) { case Bool: return equal(us, them, member); case Double: return equal(us, them, member); case Int: return equal(us, them, member); case IntList: return equal >(us, them, member); case String: return equal(us, them, member); case StringList: return equal(us, them, member); case Date: return equal(us, them, member); case DateTime: return equal(us, them, member); case Url: return equal(us, them, member); case ByteArray: return equal(us, them, member); case Variant: return equal(us, them, member); default: qFatal("Unsupported field type"); } Q_UNREACHABLE(); return false; } template static QVariant toVariant(const Subclass* us, const Member& member) { switch (member.type) { case Bool: return QVariant(*memberVariable(us, member.offset)); case Double: return QVariant(*memberVariable(us, member.offset)); case Int: return QVariant(*memberVariable(us, member.offset)); case IntList: return QVariant::fromValue(*memberVariable >(us, member.offset)); case String: return QVariant(*memberVariable(us, member.offset)); case StringList: return QVariant::fromValue(*memberVariable(us, member.offset)); case Date: return QVariant(*memberVariable(us, member.offset)); case DateTime: { // if the value was likely set as a QDate, then return it as a QDate. const QDateTime &dt(*memberVariable(us, member.offset)); return (dt.timeSpec() == Qt::LocalTime && dt.time() == QTime(0,0,0,0)) ? QVariant(dt.date()) : QVariant(dt); } case Url: return QVariant(*memberVariable(us, member.offset)); case ByteArray: return QVariant(*memberVariable(us, member.offset)); case Variant: return *memberVariable(us, member.offset); default: qFatal("Unsupported field type"); } Q_UNREACHABLE(); return false; } private: template void reinitialize(Subclass* us, const Member& member) { *memberVariable(us, member.offset) = T(); } template static void setValueFromVariant(Subclass* us, const QVariant& value, const Member& member) { *memberVariable(us, member.offset) = value.value(); } template static bool equal(const Subclass* us, const Subclass* them, const Member& member) { return *memberVariable(us, member.offset) == *memberVariable(them, member.offset); } }; template class QContactDetailBuiltinPrivate : public QContactDetailBuiltinPrivateBase { public: static const Member s_members[]; QContactDetailBuiltinPrivate(QContactDetail::DetailType type) : QContactDetailBuiltinPrivateBase(type) { } const Subclass *subclass() const { return static_cast(this); } Subclass *subclass() { return static_cast(this); } QContactDetailPrivate *clone() { return new Subclass(*subclass()); } bool operator==(const QContactDetailBuiltinPrivate& other) const { Subclass *us(subclass()); const Subclass *them(other.subclass()); for (int i = 0; i < Subclass::FieldCount; ++i) { if (!equal(us, them, s_members[i])) { return false; } } return QContactDetailPrivate::operator==(other); } template const T& memberValue(int field) const { return *memberVariable(subclass(), s_members[field].offset); } template void setMemberValue(int field, const T& value) { *memberVariable(subclass(), s_members[field].offset) = value; setHasValueBitfieldBit(true, field + BaseFieldOffset); } bool setValue(int field, const QVariant &value) { if (field < Subclass::FieldCount) { setValueFromVariant(subclass(), value, s_members[field]); setHasValueBitfieldBit(true, field + BaseFieldOffset); return true; } return QContactDetailPrivate::setValue(field, value); } bool removeValue(int field) { if (field < Subclass::FieldCount) { reinitialize(subclass(), s_members[field]); setHasValueBitfieldBit(false, field + BaseFieldOffset); return true; } return QContactDetailPrivate::removeValue(field); } bool hasValue(int field) const { if (field < Subclass::FieldCount) { return hasValueBitfieldBitSet(field + BaseFieldOffset); } return QContactDetailPrivate::hasValue(field); } QMap values() const { QMap retn = QContactDetailPrivate::values(); for (int i = 0; i < Subclass::FieldCount; ++i) { if (hasValueBitfieldBitSet(i + BaseFieldOffset)) { retn.insert(i, value(i)); } } return retn; } QVariant value(int field) const { if (field < Subclass::FieldCount) { return toVariant(subclass(), s_members[field]); } return QContactDetailPrivate::value(field); } }; QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE // allow shared data / detach for specific detail types template <> inline QTCONTACTS_PREPEND_NAMESPACE(QContactDetailPrivate) *QSharedDataPointer::clone() { return d->clone(); } QT_END_NAMESPACE #endif // QCONTACTDETAIL_P_H