File indexing completed on 2024-04-21 03:52:45
0001 /* 0002 This file is part of the kcalcore library. 0003 0004 SPDX-FileCopyrightText: 2002, 2006, 2010 David Jarvie <djarvie@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 /** 0009 @file 0010 This file is part of the API for handling calendar data and 0011 defines the CustomProperties class. 0012 0013 @brief 0014 A class to manage custom calendar properties. 0015 0016 @author David Jarvie \<djarvie@kde.org\> 0017 */ 0018 0019 #include "customproperties.h" 0020 0021 #include "kcalendarcore_debug.h" 0022 #include <QDataStream> 0023 0024 using namespace KCalendarCore; 0025 0026 //@cond PRIVATE 0027 static bool checkName(const QByteArray &name); 0028 0029 class Q_DECL_HIDDEN CustomProperties::Private 0030 { 0031 public: 0032 bool operator==(const Private &other) const; 0033 QMap<QByteArray, QString> mProperties; // custom calendar properties 0034 QMap<QByteArray, QString> mPropertyParameters; 0035 0036 // Volatile properties are not written back to the serialized format and are not compared in operator== 0037 // They are only used for runtime purposes and are not part of the payload. 0038 QMap<QByteArray, QString> mVolatileProperties; 0039 0040 bool isVolatileProperty(const QString &name) const 0041 { 0042 return name.startsWith(QLatin1String("X-KDE-VOLATILE")); 0043 } 0044 }; 0045 0046 bool CustomProperties::Private::operator==(const CustomProperties::Private &other) const 0047 { 0048 if (mProperties.count() != other.mProperties.count()) { 0049 // qCDebug(KCALCORE_LOG) << "Property count is different:" << mProperties << other.mProperties; 0050 return false; 0051 } 0052 for (auto it = mProperties.cbegin(); it != mProperties.cend(); ++it) { 0053 auto itOther = other.mProperties.constFind(it.key()); 0054 if (itOther == other.mProperties.cend() || itOther.value() != it.value()) { 0055 return false; 0056 } 0057 } 0058 for (auto it = mPropertyParameters.cbegin(); it != mPropertyParameters.cend(); ++it) { 0059 auto itOther = other.mPropertyParameters.constFind(it.key()); 0060 if (itOther == other.mPropertyParameters.cend() || itOther.value() != it.value()) { 0061 return false; 0062 } 0063 } 0064 return true; 0065 } 0066 //@endcond 0067 0068 CustomProperties::CustomProperties() 0069 : d(new Private) 0070 { 0071 } 0072 0073 CustomProperties::CustomProperties(const CustomProperties &cp) 0074 : d(new Private(*cp.d)) 0075 { 0076 } 0077 0078 CustomProperties &CustomProperties::operator=(const CustomProperties &other) 0079 { 0080 // check for self assignment 0081 if (&other == this) { 0082 return *this; 0083 } 0084 0085 *d = *other.d; 0086 return *this; 0087 } 0088 0089 CustomProperties::~CustomProperties() 0090 { 0091 delete d; 0092 } 0093 0094 bool CustomProperties::operator==(const CustomProperties &other) const 0095 { 0096 return *d == *other.d; 0097 } 0098 0099 void CustomProperties::setCustomProperty(const QByteArray &app, const QByteArray &key, const QString &value) 0100 { 0101 if (value.isNull() || key.isEmpty() || app.isEmpty()) { 0102 return; 0103 } 0104 QByteArray property = "X-KDE-" + app + '-' + key; 0105 if (!checkName(property)) { 0106 return; 0107 } 0108 customPropertyUpdate(); 0109 0110 if (d->isVolatileProperty(QLatin1String(property))) { 0111 d->mVolatileProperties[property] = value; 0112 } else { 0113 d->mProperties[property] = value; 0114 } 0115 0116 customPropertyUpdated(); 0117 } 0118 0119 void CustomProperties::removeCustomProperty(const QByteArray &app, const QByteArray &key) 0120 { 0121 removeNonKDECustomProperty(QByteArray("X-KDE-" + app + '-' + key)); 0122 } 0123 0124 QString CustomProperties::customProperty(const QByteArray &app, const QByteArray &key) const 0125 { 0126 return nonKDECustomProperty(QByteArray("X-KDE-" + app + '-' + key)); 0127 } 0128 0129 QByteArray CustomProperties::customPropertyName(const QByteArray &app, const QByteArray &key) 0130 { 0131 QByteArray property("X-KDE-" + app + '-' + key); 0132 if (!checkName(property)) { 0133 return QByteArray(); 0134 } 0135 return property; 0136 } 0137 0138 void CustomProperties::setNonKDECustomProperty(const QByteArray &name, const QString &value, const QString ¶meters) 0139 { 0140 if (value.isNull() || !checkName(name)) { 0141 return; 0142 } 0143 customPropertyUpdate(); 0144 if (d->isVolatileProperty(QLatin1String(name))) { 0145 d->mVolatileProperties[name] = value; 0146 } else { 0147 d->mProperties[name] = value; 0148 d->mPropertyParameters[name] = parameters; 0149 } 0150 customPropertyUpdated(); 0151 } 0152 void CustomProperties::removeNonKDECustomProperty(const QByteArray &name) 0153 { 0154 if (d->mProperties.contains(name)) { 0155 customPropertyUpdate(); 0156 d->mProperties.remove(name); 0157 d->mPropertyParameters.remove(name); 0158 customPropertyUpdated(); 0159 } else if (d->mVolatileProperties.contains(name)) { 0160 customPropertyUpdate(); 0161 d->mVolatileProperties.remove(name); 0162 customPropertyUpdated(); 0163 } 0164 } 0165 0166 QString CustomProperties::nonKDECustomProperty(const QByteArray &name) const 0167 { 0168 return d->isVolatileProperty(QLatin1String(name)) ? d->mVolatileProperties.value(name) : d->mProperties.value(name); 0169 } 0170 0171 QString CustomProperties::nonKDECustomPropertyParameters(const QByteArray &name) const 0172 { 0173 return d->mPropertyParameters.value(name); 0174 } 0175 0176 void CustomProperties::setCustomProperties(const QMap<QByteArray, QString> &properties) 0177 { 0178 bool changed = false; 0179 for (auto it = properties.cbegin(); it != properties.cend(); ++it) { 0180 // Validate the property name and convert any null string to empty string 0181 if (checkName(it.key())) { 0182 if (d->isVolatileProperty(QLatin1String(it.key()))) { 0183 d->mVolatileProperties[it.key()] = it.value().isNull() ? QLatin1String("") : it.value(); 0184 } else { 0185 d->mProperties[it.key()] = it.value().isNull() ? QLatin1String("") : it.value(); 0186 } 0187 if (!changed) { 0188 customPropertyUpdate(); 0189 } 0190 changed = true; 0191 } 0192 } 0193 if (changed) { 0194 customPropertyUpdated(); 0195 } 0196 } 0197 0198 QMap<QByteArray, QString> CustomProperties::customProperties() const 0199 { 0200 QMap<QByteArray, QString> result = d->mProperties; 0201 for (auto it = d->mVolatileProperties.begin(), end = d->mVolatileProperties.end(); it != end; ++it) { 0202 result.insert(it.key(), it.value()); 0203 } 0204 0205 return result; 0206 } 0207 0208 void CustomProperties::customPropertyUpdate() 0209 { 0210 } 0211 0212 void CustomProperties::customPropertyUpdated() 0213 { 0214 } 0215 0216 //@cond PRIVATE 0217 bool checkName(const QByteArray &name) 0218 { 0219 // Check that the property name starts with 'X-' and contains 0220 // only the permitted characters 0221 const char *n = name.constData(); 0222 int len = name.length(); 0223 if (len < 2 || n[0] != 'X' || n[1] != '-') { 0224 return false; 0225 } 0226 for (int i = 2; i < len; ++i) { 0227 char ch = n[i]; 0228 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-') { 0229 continue; 0230 } 0231 return false; // invalid character found 0232 } 0233 return true; 0234 } 0235 //@endcond 0236 0237 QDataStream &KCalendarCore::operator<<(QDataStream &stream, const KCalendarCore::CustomProperties &properties) 0238 { 0239 return stream << properties.d->mProperties << properties.d->mPropertyParameters; 0240 } 0241 0242 QDataStream &KCalendarCore::operator>>(QDataStream &stream, KCalendarCore::CustomProperties &properties) 0243 { 0244 properties.d->mVolatileProperties.clear(); 0245 return stream >> properties.d->mProperties >> properties.d->mPropertyParameters; 0246 }