1 | /* |
2 | This file is part of the kcalcore library. |
3 | |
4 | Copyright (c) 2002,2006,2010 David Jarvie <djarvie@kde.org> |
5 | |
6 | This library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Library General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2 of the License, or (at your option) any later version. |
10 | |
11 | This library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Library General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Library General Public License |
17 | along with this library; see the file COPYING.LIB. If not, write to |
18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | Boston, MA 02110-1301, USA. |
20 | */ |
21 | /** |
22 | @file |
23 | This file is part of the API for handling calendar data and |
24 | defines the CustomProperties class. |
25 | |
26 | @brief |
27 | A class to manage custom calendar properties. |
28 | |
29 | @author David Jarvie \<djarvie@kde.org\> |
30 | */ |
31 | |
32 | #include "customproperties.h" |
33 | |
34 | #include <QDataStream> |
35 | #include <KDebug> |
36 | |
37 | using namespace KCalCore; |
38 | |
39 | //@cond PRIVATE |
40 | static bool checkName(const QByteArray &name); |
41 | |
42 | class CustomProperties::Private |
43 | { |
44 | public: |
45 | bool operator==(const Private &other) const; |
46 | QMap<QByteArray, QString> mProperties; // custom calendar properties |
47 | QMap<QByteArray, QString> mPropertyParameters; |
48 | |
49 | // Volatile properties are not written back to the serialized format and are not compared in operator== |
50 | // They are only used for runtime purposes and are not part of the payload. |
51 | QMap<QByteArray, QString> mVolatileProperties; |
52 | |
53 | |
54 | bool isVolatileProperty(const QString &name) const |
55 | { |
56 | return name.startsWith(QLatin1String("X-KDE-VOLATILE" )); |
57 | } |
58 | }; |
59 | |
60 | bool CustomProperties::Private::operator==(const CustomProperties::Private &other) const |
61 | { |
62 | if (mProperties.count() != other.mProperties.count()) { |
63 | // kDebug() << "Property count is different:" << mProperties << other.mProperties; |
64 | return false; |
65 | } |
66 | for (QMap<QByteArray, QString>::ConstIterator it = mProperties.begin(); |
67 | it != mProperties.end(); ++it) { |
68 | QMap<QByteArray, QString>::ConstIterator itOther = |
69 | other.mProperties.find(it.key()); |
70 | if (itOther == other.mProperties.end() || itOther.value() != it.value()) { |
71 | return false; |
72 | } |
73 | } |
74 | for (QMap<QByteArray, QString>::ConstIterator it = mPropertyParameters.begin(); |
75 | it != mPropertyParameters.end(); ++it) { |
76 | QMap<QByteArray, QString>::ConstIterator itOther = |
77 | other.mPropertyParameters.find(it.key()); |
78 | if (itOther == other.mPropertyParameters.end() || itOther.value() != it.value()) { |
79 | return false; |
80 | } |
81 | } |
82 | return true; |
83 | } |
84 | //@endcond |
85 | |
86 | CustomProperties::CustomProperties() |
87 | : d(new Private) |
88 | { |
89 | } |
90 | |
91 | CustomProperties::CustomProperties(const CustomProperties &cp) |
92 | : d(new Private(*cp.d)) |
93 | { |
94 | } |
95 | |
96 | CustomProperties &CustomProperties::operator=(const CustomProperties &other) |
97 | { |
98 | // check for self assignment |
99 | if (&other == this) { |
100 | return *this; |
101 | } |
102 | |
103 | *d = *other.d; |
104 | return *this; |
105 | } |
106 | |
107 | CustomProperties::~CustomProperties() |
108 | { |
109 | delete d; |
110 | } |
111 | |
112 | bool CustomProperties::operator==(const CustomProperties &other) const |
113 | { |
114 | return *d == *other.d; |
115 | } |
116 | |
117 | void CustomProperties::setCustomProperty(const QByteArray &app, const QByteArray &key, |
118 | const QString &value) |
119 | { |
120 | if (value.isNull() || key.isEmpty() || app.isEmpty()) { |
121 | return; |
122 | } |
123 | QByteArray property = "X-KDE-" + app + '-' + key; |
124 | if (!checkName(property)) { |
125 | return; |
126 | } |
127 | customPropertyUpdate(); |
128 | |
129 | if (d->isVolatileProperty(property)) { |
130 | d->mVolatileProperties[property] = value; |
131 | } else { |
132 | d->mProperties[property] = value; |
133 | } |
134 | |
135 | customPropertyUpdated(); |
136 | } |
137 | |
138 | void CustomProperties::removeCustomProperty(const QByteArray &app, const QByteArray &key) |
139 | { |
140 | removeNonKDECustomProperty(QByteArray("X-KDE-" + app + '-' + key)); |
141 | } |
142 | |
143 | QString CustomProperties::customProperty(const QByteArray &app, const QByteArray &key) const |
144 | { |
145 | return nonKDECustomProperty(QByteArray("X-KDE-" + app + '-' + key)); |
146 | } |
147 | |
148 | QByteArray CustomProperties::customPropertyName(const QByteArray &app, const QByteArray &key) |
149 | { |
150 | QByteArray property("X-KDE-" + app + '-' + key); |
151 | if (!checkName(property)) { |
152 | return QByteArray(); |
153 | } |
154 | return property; |
155 | } |
156 | |
157 | void CustomProperties::setNonKDECustomProperty(const QByteArray &name, const QString &value, |
158 | const QString ¶meters) |
159 | { |
160 | if (value.isNull() || !checkName(name)) { |
161 | return; |
162 | } |
163 | customPropertyUpdate(); |
164 | d->mProperties[name] = value; |
165 | d->mPropertyParameters[name] = parameters; |
166 | customPropertyUpdated(); |
167 | } |
168 | void CustomProperties::removeNonKDECustomProperty(const QByteArray &name) |
169 | { |
170 | if (d->mProperties.contains(name)) { |
171 | customPropertyUpdate(); |
172 | d->mProperties.remove(name); |
173 | d->mPropertyParameters.remove(name); |
174 | customPropertyUpdated(); |
175 | } else if (d->mVolatileProperties.contains(name)) { |
176 | customPropertyUpdate(); |
177 | d->mVolatileProperties.remove(name); |
178 | customPropertyUpdated(); |
179 | } |
180 | } |
181 | |
182 | QString CustomProperties::nonKDECustomProperty(const QByteArray &name) const |
183 | { |
184 | return d->isVolatileProperty(name) ? d->mVolatileProperties.value(name) : d->mProperties.value(name); |
185 | } |
186 | |
187 | QString CustomProperties::nonKDECustomPropertyParameters(const QByteArray &name) const |
188 | { |
189 | return d->mPropertyParameters.value(name); |
190 | } |
191 | |
192 | void CustomProperties::setCustomProperties(const QMap<QByteArray, QString> &properties) |
193 | { |
194 | bool changed = false; |
195 | for (QMap<QByteArray, QString>::ConstIterator it = properties.begin(); |
196 | it != properties.end(); ++it) { |
197 | // Validate the property name and convert any null string to empty string |
198 | if (checkName(it.key())) { |
199 | if (d->isVolatileProperty(it.key())) { |
200 | d->mVolatileProperties[it.key()] = it.value().isNull() ? QLatin1String("" ) : it.value(); |
201 | } else { |
202 | d->mProperties[it.key()] = it.value().isNull() ? QLatin1String("" ) : it.value(); |
203 | } |
204 | if (!changed) { |
205 | customPropertyUpdate(); |
206 | } |
207 | changed = true; |
208 | } |
209 | } |
210 | if (changed) { |
211 | customPropertyUpdated(); |
212 | } |
213 | } |
214 | |
215 | QMap<QByteArray, QString> CustomProperties::customProperties() const |
216 | { |
217 | QMap<QByteArray, QString> result; |
218 | result.unite(d->mProperties); |
219 | result.unite(d->mVolatileProperties); |
220 | |
221 | return result; |
222 | } |
223 | |
224 | void CustomProperties::customPropertyUpdate() |
225 | { |
226 | } |
227 | |
228 | void CustomProperties::customPropertyUpdated() |
229 | { |
230 | } |
231 | |
232 | void CustomProperties::virtual_hook(int id, void *data) |
233 | { |
234 | Q_UNUSED(id); |
235 | Q_UNUSED(data); |
236 | Q_ASSERT(false); |
237 | } |
238 | |
239 | //@cond PRIVATE |
240 | bool checkName(const QByteArray &name) |
241 | { |
242 | // Check that the property name starts with 'X-' and contains |
243 | // only the permitted characters |
244 | const char *n = name; |
245 | int len = name.length(); |
246 | if (len < 2 || n[0] != 'X' || n[1] != '-') { |
247 | return false; |
248 | } |
249 | for (int i = 2; i < len; ++i) { |
250 | char ch = n[i]; |
251 | if ((ch >= 'A' && ch <= 'Z') || |
252 | (ch >= 'a' && ch <= 'z') || |
253 | (ch >= '0' && ch <= '9') || |
254 | ch == '-') { |
255 | continue; |
256 | } |
257 | return false; // invalid character found |
258 | } |
259 | return true; |
260 | } |
261 | //@endcond |
262 | |
263 | QDataStream &KCalCore::operator<<(QDataStream &stream, |
264 | const KCalCore::CustomProperties &properties) |
265 | { |
266 | return stream << properties.d->mProperties |
267 | << properties.d->mPropertyParameters; |
268 | } |
269 | |
270 | QDataStream &KCalCore::operator>>(QDataStream &stream, |
271 | KCalCore::CustomProperties &properties) |
272 | { |
273 | properties.d->mVolatileProperties.clear(); |
274 | return stream >> properties.d->mProperties |
275 | >> properties.d->mPropertyParameters; |
276 | } |
277 | |
278 | |