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