1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> |
4 | SPDX-FileCopyrightText: 1999-2000 Preston Brown <pbrown@kde.org> |
5 | SPDX-FileCopyrightText: 1996-2000 Matthias Kalle Dalheimer <kalle@kde.org> |
6 | |
7 | SPDX-License-Identifier: LGPL-2.0-or-later |
8 | */ |
9 | |
10 | #ifndef KCONFIGDATA_P_H |
11 | #define KCONFIGDATA_P_H |
12 | |
13 | #include <QByteArray> |
14 | #include <QDebug> |
15 | #include <QString> |
16 | #include <map> |
17 | |
18 | /** |
19 | * map/dict/list config node entry. |
20 | * @internal |
21 | */ |
22 | struct KEntry { |
23 | /** Constructor. @internal */ |
24 | KEntry() |
25 | : mValue() |
26 | , bDirty(false) |
27 | , bGlobal(false) |
28 | , bImmutable(false) |
29 | , bDeleted(false) |
30 | , bExpand(false) |
31 | , bReverted(false) |
32 | , bLocalizedCountry(false) |
33 | , bNotify(false) |
34 | , bOverridesGlobal(false) |
35 | { |
36 | } |
37 | /** @internal */ |
38 | QByteArray mValue; |
39 | /** |
40 | * Must the entry be written back to disk? |
41 | */ |
42 | bool bDirty : 1; |
43 | /** |
44 | * Entry should be written to the global config file |
45 | */ |
46 | bool bGlobal : 1; |
47 | /** |
48 | * Entry can not be modified. |
49 | */ |
50 | bool bImmutable : 1; |
51 | /** |
52 | * Entry has been deleted. |
53 | */ |
54 | bool bDeleted : 1; |
55 | /** |
56 | * Whether to apply dollar expansion or not. |
57 | */ |
58 | bool bExpand : 1; |
59 | /** |
60 | * Entry has been reverted to its default value (from a more global file). |
61 | */ |
62 | bool bReverted : 1; |
63 | /** |
64 | * Entry is for a localized key. If @c false the value references just language e.g. "de", |
65 | * if @c true the value references language and country, e.g. "de_DE". |
66 | **/ |
67 | bool bLocalizedCountry : 1; |
68 | |
69 | bool bNotify : 1; |
70 | |
71 | /** |
72 | * Entry will need to be written on a non global file even if it matches default value |
73 | */ |
74 | bool bOverridesGlobal : 1; |
75 | }; |
76 | |
77 | Q_DECLARE_TYPEINFO(KEntry, Q_RELOCATABLE_TYPE); |
78 | |
79 | // These operators are used to check whether an entry which is about |
80 | // to be written equals the previous value. As such, this intentionally |
81 | // omits the dirty/notify flag from the comparison. |
82 | inline bool operator==(const KEntry &k1, const KEntry &k2) |
83 | { |
84 | /* clang-format off */ |
85 | return k1.bGlobal == k2.bGlobal |
86 | && k1.bImmutable == k2.bImmutable |
87 | && k1.bDeleted == k2.bDeleted |
88 | && k1.bExpand == k2.bExpand |
89 | && k1.mValue == k2.mValue; |
90 | /* clang-format on */ |
91 | } |
92 | |
93 | inline bool operator!=(const KEntry &k1, const KEntry &k2) |
94 | { |
95 | return !(k1 == k2); |
96 | } |
97 | |
98 | /** |
99 | * key structure holding both the actual key and the group |
100 | * to which it belongs. |
101 | * @internal |
102 | */ |
103 | struct KEntryKey { |
104 | /** Constructor. @internal */ |
105 | KEntryKey(const QString &_group = QString(), const QByteArray &_key = QByteArray(), bool isLocalized = false, bool isDefault = false) |
106 | : mGroup(_group) |
107 | , mKey(_key) |
108 | , bLocal(isLocalized) |
109 | , bDefault(isDefault) |
110 | , bRaw(false) |
111 | { |
112 | } |
113 | /** |
114 | * The "group" to which this EntryKey belongs |
115 | */ |
116 | QString mGroup; |
117 | /** |
118 | * The _actual_ key of the entry in question |
119 | */ |
120 | QByteArray mKey; |
121 | /** |
122 | * Entry is localised or not |
123 | */ |
124 | bool bLocal : 1; |
125 | /** |
126 | * Entry indicates if this is a default value. |
127 | */ |
128 | bool bDefault : 1; |
129 | /** @internal |
130 | * Key is a raw unprocessed key. |
131 | * @warning this should only be set during merging, never for normal use. |
132 | */ |
133 | bool bRaw : 1; |
134 | }; |
135 | |
136 | Q_DECLARE_TYPEINFO(KEntryKey, Q_RELOCATABLE_TYPE); |
137 | |
138 | /** |
139 | * Compares two KEntryKeys (needed for std::map). The order is localized, localized-default, |
140 | * non-localized, non-localized-default |
141 | * @internal |
142 | */ |
143 | inline bool operator<(const KEntryKey &k1, const KEntryKey &k2) |
144 | { |
145 | int result = k1.mGroup.compare(s: k2.mGroup); |
146 | if (result != 0) { |
147 | return result < 0; |
148 | } |
149 | |
150 | result = k1.mKey.compare(a: k2.mKey); |
151 | if (result != 0) { |
152 | return result < 0; |
153 | } |
154 | |
155 | if (k1.bLocal != k2.bLocal) { |
156 | return k1.bLocal; |
157 | } |
158 | return (!k1.bDefault && k2.bDefault); |
159 | } |
160 | |
161 | /** |
162 | * Light-weight view variant of KEntryKey. |
163 | * Used for look-up in the map. |
164 | * @internal |
165 | */ |
166 | struct KEntryKeyView { |
167 | /** Constructor. @internal */ |
168 | KEntryKeyView(QStringView _group, QAnyStringView _key, bool isLocalized = false, bool isDefault = false) |
169 | : mGroup(_group) |
170 | , mKey(_key) |
171 | , bLocal(isLocalized) |
172 | , bDefault(isDefault) |
173 | { |
174 | } |
175 | /** |
176 | * The "group" to which this EntryKey belongs |
177 | */ |
178 | const QStringView mGroup; |
179 | /** |
180 | * The _actual_ key of the entry in question |
181 | */ |
182 | const QAnyStringView mKey; |
183 | /** |
184 | * Entry is localised or not |
185 | */ |
186 | bool bLocal : 1; |
187 | /** |
188 | * Entry indicates if this is a default value. |
189 | */ |
190 | bool bDefault : 1; |
191 | }; |
192 | |
193 | template<typename TEntryKey1, typename TEntryKey2> |
194 | bool compareEntryKeyViews(const TEntryKey1 &k1, const TEntryKey2 &k2) |
195 | { |
196 | int result = k1.mGroup.compare(k2.mGroup); |
197 | if (result != 0) { |
198 | return result < 0; |
199 | } |
200 | |
201 | result = QAnyStringView::compare(lhs: k1.mKey, rhs: k2.mKey); |
202 | if (result != 0) { |
203 | return result < 0; |
204 | } |
205 | |
206 | if (k1.bLocal != k2.bLocal) { |
207 | return k1.bLocal; |
208 | } |
209 | return (!k1.bDefault && k2.bDefault); |
210 | } |
211 | |
212 | inline bool operator<(const KEntryKeyView &k1, const KEntryKey &k2) |
213 | { |
214 | return compareEntryKeyViews(k1, k2); |
215 | } |
216 | |
217 | inline bool operator<(const KEntryKey &k1, const KEntryKeyView &k2) |
218 | { |
219 | return compareEntryKeyViews(k1, k2); |
220 | } |
221 | |
222 | /** |
223 | * Struct to use as Compare type with std::map. |
224 | * To enable usage of KEntryKeyView for look-up in the map |
225 | * via the template find() overloads. |
226 | * @internal |
227 | */ |
228 | struct KEntryKeyCompare { |
229 | using is_transparent = void; |
230 | |
231 | bool operator()(const KEntryKey &k1, const KEntryKey &k2) const |
232 | { |
233 | return (k1 < k2); |
234 | } |
235 | |
236 | bool operator()(const KEntryKeyView &k1, const KEntryKey &k2) const |
237 | { |
238 | return (k1 < k2); |
239 | } |
240 | |
241 | bool operator()(const KEntryKey &k1, const KEntryKeyView &k2) const |
242 | { |
243 | return (k1 < k2); |
244 | } |
245 | }; |
246 | |
247 | /** |
248 | * Returns the minimum key that has @a mGroup == @p group. |
249 | * |
250 | * @note The returned "minimum key" is consistent with KEntryKey's operator<(). |
251 | * The return value of this function can be passed to KEntryMap::lowerBound(). |
252 | */ |
253 | inline KEntryKeyView minimumGroupKeyView(const QString &group) |
254 | { |
255 | return KEntryKeyView(group, QAnyStringView{}, true, false); |
256 | } |
257 | |
258 | QDebug operator<<(QDebug dbg, const KEntryKey &key); |
259 | QDebug operator<<(QDebug dbg, const KEntry &entry); |
260 | |
261 | /** |
262 | * \relates KEntry |
263 | * type specifying a map of entries (key,value pairs). |
264 | * The keys are actually a key in a particular config file group together |
265 | * with the group name. |
266 | * @internal |
267 | */ |
268 | class KEntryMap : public std::map<KEntryKey, KEntry, KEntryKeyCompare> |
269 | { |
270 | public: |
271 | enum SearchFlag { |
272 | SearchDefaults = 1, |
273 | SearchLocalized = 2, |
274 | }; |
275 | Q_DECLARE_FLAGS(SearchFlags, SearchFlag) |
276 | |
277 | enum EntryOption { |
278 | EntryDirty = 1, |
279 | EntryGlobal = 2, |
280 | EntryImmutable = 4, |
281 | EntryDeleted = 8, |
282 | EntryExpansion = 16, |
283 | EntryRawKey = 32, |
284 | EntryLocalizedCountry = 64, |
285 | EntryNotify = 128, |
286 | EntryDefault = (SearchDefaults << 16), |
287 | EntryLocalized = (SearchLocalized << 16), |
288 | }; |
289 | Q_DECLARE_FLAGS(EntryOptions, EntryOption) |
290 | |
291 | iterator findExactEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags()); |
292 | |
293 | iterator findEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags()); |
294 | |
295 | const_iterator findEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags()) const |
296 | { |
297 | return constFindEntry(group, key, flags); |
298 | } |
299 | |
300 | const_iterator constFindEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags()) const; |
301 | |
302 | /** |
303 | * Returns true if the entry gets dirtied or false in other case |
304 | */ |
305 | bool setEntry(const QString &group, const QByteArray &key, const QByteArray &value, EntryOptions options); |
306 | |
307 | void setEntry(const QString &group, const QByteArray &key, const QString &value, EntryOptions options) |
308 | { |
309 | setEntry(group, key, value: value.toUtf8(), options); |
310 | } |
311 | |
312 | QString getEntry(const QString &group, |
313 | QAnyStringView key, |
314 | const QString &defaultValue = QString(), |
315 | SearchFlags flags = SearchFlags(), |
316 | bool *expand = nullptr) const; |
317 | |
318 | bool hasEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags()) const; |
319 | |
320 | bool getEntryOption(const const_iterator &it, EntryOption option) const; |
321 | bool getEntryOption(const QString &group, QAnyStringView key, SearchFlags flags, EntryOption option) const |
322 | { |
323 | return getEntryOption(it: findEntry(group, key, flags), option); |
324 | } |
325 | |
326 | void setEntryOption(iterator it, EntryOption option, bool bf); |
327 | void setEntryOption(const QString &group, QAnyStringView key, SearchFlags flags, EntryOption option, bool bf) |
328 | { |
329 | setEntryOption(it: findEntry(group, key, flags), option, bf); |
330 | } |
331 | |
332 | bool revertEntry(const QString &group, QAnyStringView key, EntryOptions options, SearchFlags flags = SearchFlags()); |
333 | |
334 | template<typename ConstIteratorUser> |
335 | void forEachEntryWhoseGroupStartsWith(const QString &groupPrefix, ConstIteratorUser callback) const |
336 | { |
337 | for (auto it = lower_bound(x: minimumGroupKeyView(group: groupPrefix)), end = cend(); it != end && it->first.mGroup.startsWith(s: groupPrefix); ++it) { |
338 | callback(it); |
339 | } |
340 | } |
341 | |
342 | template<typename ConstIteratorPredicate> |
343 | bool anyEntryWhoseGroupStartsWith(const QString &groupPrefix, ConstIteratorPredicate predicate) const |
344 | { |
345 | for (auto it = lower_bound(x: minimumGroupKeyView(group: groupPrefix)), end = cend(); it != end && it->first.mGroup.startsWith(s: groupPrefix); ++it) { |
346 | if (predicate(it)) { |
347 | return true; |
348 | } |
349 | } |
350 | return false; |
351 | } |
352 | |
353 | template<typename ConstIteratorUser> |
354 | void forEachEntryOfGroup(const QString &theGroup, ConstIteratorUser callback) const |
355 | { |
356 | const auto theEnd = cend(); |
357 | auto it = constFindEntry(group: theGroup); |
358 | if (it != theEnd) { |
359 | ++it; // advance past the special group entry marker |
360 | |
361 | for (; (it != theEnd) && (it->first.mGroup == theGroup); ++it) { |
362 | callback(it); |
363 | } |
364 | } |
365 | } |
366 | }; |
367 | Q_DECLARE_OPERATORS_FOR_FLAGS(KEntryMap::SearchFlags) |
368 | Q_DECLARE_OPERATORS_FOR_FLAGS(KEntryMap::EntryOptions) |
369 | |
370 | /** |
371 | * \relates KEntry |
372 | * type for iterating over keys in a KEntryMap in sorted order. |
373 | * @internal |
374 | */ |
375 | typedef KEntryMap::iterator KEntryMapIterator; |
376 | |
377 | /** |
378 | * \relates KEntry |
379 | * type for iterating over keys in a KEntryMap in sorted order. |
380 | * It is const, thus you cannot change the entries in the iterator, |
381 | * only examine them. |
382 | * @internal |
383 | */ |
384 | typedef KEntryMap::const_iterator KEntryMapConstIterator; |
385 | |
386 | #endif |
387 | |