1 /*
2 * SPDX-FileCopyrightText: 2015-2015 CSSlayer <wengxt@gmail.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 */
7 #include "rawconfig.h"
8 #include <list>
9 #include <unordered_map>
10 #include <utility>
11 #include "fcitx-utils/stringutils.h"
12
13 namespace fcitx {
14
15 template <typename K, typename V, class Hash = std::hash<K>,
16 class Pred = std::equal_to<K>>
17 class OrderedMap {
18 private:
19 std::list<std::pair<const K, V>> order_;
20 std::unordered_map<K, typename decltype(order_)::iterator, Hash, Pred> map_;
21
22 public:
23 typedef decltype(map_) map_type;
24 typedef decltype(order_) list_type;
25 typedef typename map_type::key_type key_type;
26 typedef typename list_type::value_type value_type;
27 typedef typename value_type::second_type mapped_type;
28
29 typedef typename list_type::pointer pointer;
30 typedef typename list_type::const_pointer const_pointer;
31 typedef typename list_type::reference reference;
32 typedef typename list_type::const_reference const_reference;
33 typedef typename list_type::iterator iterator;
34 typedef typename list_type::const_iterator const_iterator;
35 typedef typename list_type::size_type size_type;
36 typedef typename list_type::difference_type difference_type;
37
38 OrderedMap() = default;
OrderedMap(const OrderedMap & other)39 OrderedMap(const OrderedMap &other) : order_(other.order_) { fillMap(); }
40
41 /// Move constructor.
OrderedMap(OrderedMap && other)42 OrderedMap(OrderedMap &&other) noexcept { operator=(std::move(other)); }
43
OrderedMap(std::initializer_list<value_type> l)44 OrderedMap(std::initializer_list<value_type> l) : order_(l) { fillMap(); }
45
46 template <typename InputIterator>
OrderedMap(InputIterator first,InputIterator last)47 OrderedMap(InputIterator first, InputIterator last) : order_(first, last) {
48 fillMap();
49 }
50
51 /// Copy assignment operator.
operator =(const OrderedMap & other)52 OrderedMap &operator=(const OrderedMap &other) {
53 order_ = list_type(other.order_.begin(), other.order_.end());
54 fillMap();
55 return *this;
56 }
57
58 /// Move assignment operator.
operator =(OrderedMap && other)59 OrderedMap &operator=(OrderedMap &&other) noexcept {
60 using std::swap;
61 swap(order_, other.order_);
62 swap(map_, other.map_);
63 other.order_.clear();
64 other.map_.clear();
65 return *this;
66 }
67
empty() const68 bool empty() const noexcept { return order_.empty(); }
69
size() const70 size_type size() const noexcept { return order_.size(); }
71
begin()72 iterator begin() noexcept { return order_.begin(); }
73
begin() const74 const_iterator begin() const noexcept { return order_.begin(); }
75
cbegin() const76 const_iterator cbegin() const noexcept { return order_.begin(); }
77
end()78 iterator end() noexcept { return order_.end(); }
79
end() const80 const_iterator end() const noexcept { return order_.end(); }
81
cend() const82 const_iterator cend() const noexcept { return order_.end(); }
83
84 template <typename... _Args>
emplace(_Args &&...__args)85 std::pair<iterator, bool> emplace(_Args &&...__args) {
86 order_.emplace_back(std::forward<_Args>(__args)...);
87 auto iter = std::prev(order_.end());
88 auto mapResult = map_.emplace(iter->first, iter);
89 if (mapResult.second) {
90 return {iter, true};
91 }
92 order_.erase(iter);
93 iter = mapResult.first->second;
94 return {iter, false};
95 }
96
insert(const value_type & v)97 std::pair<iterator, bool> insert(const value_type &v) { return emplace(v); }
98
erase(const_iterator position)99 iterator erase(const_iterator position) {
100 map_.erase(position->first);
101 return order_.erase(position);
102 }
103
erase(iterator position)104 iterator erase(iterator position) {
105 map_.erase(position->first);
106 return order_.erase(position);
107 }
108
erase(const key_type & k)109 size_type erase(const key_type &k) {
110 auto iter = map_.find(k);
111 if (iter != map_.end()) {
112 order_.erase(iter->second);
113 map_.erase(iter);
114 return 1;
115 }
116 return 0;
117 }
118
clear()119 void clear() noexcept {
120 order_.clear();
121 map_.clear();
122 }
123
find(const key_type & k)124 iterator find(const key_type &k) {
125 auto iter = map_.find(k);
126 if (iter != map_.end()) {
127 return iter->second;
128 }
129 return order_.end();
130 }
131
find(const key_type & k) const132 const_iterator find(const key_type &k) const {
133 auto iter = map_.find(k);
134 if (iter != map_.end()) {
135 return iter->second;
136 }
137 return order_.end();
138 }
139
count(const key_type & k) const140 size_type count(const key_type &k) const { return map_.count(k); }
141
operator [](const key_type & k)142 mapped_type &operator[](const key_type &k) {
143 auto iter = find(k);
144 if (iter != end()) {
145 return iter->second;
146 }
147 auto result = emplace(k, mapped_type());
148 return result.first->second;
149 }
150
operator [](key_type && k)151 mapped_type &operator[](key_type &&k) {
152 auto iter = find(k);
153 if (iter != end()) {
154 return iter->second;
155 }
156 auto result = emplace(std::move(k), value_type());
157 return result.first->second;
158 }
159
160 private:
fillMap()161 void fillMap() {
162 for (auto iter = order_.begin(), end = order_.end(); iter != end;
163 iter++) {
164 map_[iter->first] = iter;
165 }
166 }
167 };
168
169 class RawConfigPrivate : public QPtrHolder<RawConfig> {
170 public:
RawConfigPrivate(RawConfig * q,std::string _name)171 RawConfigPrivate(RawConfig *q, std::string _name)
172 : QPtrHolder(q), name_(std::move(_name)), lineNumber_(0) {}
RawConfigPrivate(RawConfig * q,const RawConfigPrivate & other)173 RawConfigPrivate(RawConfig *q, const RawConfigPrivate &other)
174 : QPtrHolder(q), value_(other.value_), comment_(other.comment_),
175 lineNumber_(other.lineNumber_) {}
176
operator =(const RawConfigPrivate & other)177 RawConfigPrivate &operator=(const RawConfigPrivate &other) {
178 if (&other == this) {
179 return *this;
180 }
181 // There is no need to copy "name_", because name refers to its parent.
182 // Make a copy of value, comment, and lineNumber, because this "other"
183 // might be our parent.
184 auto value = other.value_;
185 auto comment = other.comment_;
186 auto lineNumber = other.lineNumber_;
187 OrderedMap<std::string, std::shared_ptr<RawConfig>> newSubItems;
188 for (const auto &item : other.subItems_) {
189 auto result = newSubItems[item.first] =
190 q_func()->createSub(item.second->name());
191 *result = *item.second;
192 }
193 value_ = std::move(value);
194 comment_ = std::move(comment);
195 lineNumber_ = lineNumber;
196 detachSubItems();
197 subItems_ = std::move(newSubItems);
198 return *this;
199 }
200
getNonexistentRawConfig(RawConfig * q,const std::string & key)201 std::shared_ptr<RawConfig> getNonexistentRawConfig(RawConfig *q,
202 const std::string &key) {
203 auto result = subItems_[key] = q->createSub(key);
204 return result;
205 }
206
207 static std::shared_ptr<const RawConfig>
getNonexistentRawConfig(const RawConfig *,const std::string &)208 getNonexistentRawConfig(const RawConfig *, const std::string &) {
209 return nullptr;
210 }
211
212 template <typename T, typename U>
213 static std::shared_ptr<T>
getRawConfigHelper(T & that,const std::string & path,U callback)214 getRawConfigHelper(T &that, const std::string &path, U callback) {
215 auto *cur = &that;
216 std::shared_ptr<T> result;
217 for (std::string::size_type pos = 0, new_pos = path.find('/', pos);
218 pos != std::string::npos && cur;
219 pos = ((std::string::npos == new_pos) ? new_pos : (new_pos + 1)),
220 new_pos = path.find('/', pos)) {
221 auto key = path.substr(pos, (std::string::npos == new_pos)
222 ? new_pos
223 : (new_pos - pos));
224 auto iter = cur->d_func()->subItems_.find(key);
225 if (iter == cur->d_func()->subItems_.end()) {
226 result = cur->d_func()->getNonexistentRawConfig(cur, key);
227 } else {
228 result = iter->second;
229 }
230 cur = result.get();
231
232 if (cur) {
233 callback(*cur, path.substr(0, new_pos));
234 }
235 }
236 return result;
237 }
238
239 template <typename T>
240 static bool
visitHelper(T & that,std::function<bool (T &,const std::string & path)> callback,bool recursive,const std::string & pathPrefix)241 visitHelper(T &that,
242 std::function<bool(T &, const std::string &path)> callback,
243 bool recursive, const std::string &pathPrefix) {
244 auto d = that.d_func();
245 for (const auto &pair : d->subItems_) {
246 std::shared_ptr<T> item = pair.second;
247 auto newPathPrefix = pathPrefix.empty()
248 ? item->name()
249 : pathPrefix + "/" + item->name();
250 if (!callback(*item, newPathPrefix)) {
251 return false;
252 }
253 if (recursive) {
254 if (!visitHelper(*item, callback, recursive, newPathPrefix)) {
255 return false;
256 }
257 }
258 }
259 return true;
260 }
261
detachSubItems()262 void detachSubItems() {
263 for (const auto &pair : subItems_) {
264 pair.second->d_func()->parent_ = nullptr;
265 }
266 }
267
268 RawConfig *parent_ = nullptr;
269 const std::string name_;
270 std::string value_;
271 std::string comment_;
272 OrderedMap<std::string, std::shared_ptr<RawConfig>> subItems_;
273 unsigned int lineNumber_;
274 };
275
RawConfig()276 RawConfig::RawConfig() : RawConfig("") {}
277
RawConfig(std::string name)278 RawConfig::RawConfig(std::string name)
279 : d_ptr(std::make_unique<RawConfigPrivate>(this, std::move(name))) {}
280
~RawConfig()281 RawConfig::~RawConfig() {
282 FCITX_D();
283 d->detachSubItems();
284 }
285
RawConfig(const RawConfig & other)286 RawConfig::RawConfig(const RawConfig &other)
287 : d_ptr(std::make_unique<RawConfigPrivate>(this, *other.d_ptr)) {
288 for (const auto &item : other.d_func()->subItems_) {
289 *get(item.first, true) = *item.second;
290 }
291 }
operator =(const RawConfig & other)292 RawConfig &RawConfig::operator=(const RawConfig &other) {
293 *d_ptr = *other.d_ptr;
294 return *this;
295 }
296
get(const std::string & path,bool create)297 std::shared_ptr<RawConfig> RawConfig::get(const std::string &path,
298 bool create) {
299 auto dummy = [](const RawConfig &, const std::string &) {};
300 if (create) {
301 return RawConfigPrivate::getRawConfigHelper(*this, path, dummy);
302 }
303 return std::const_pointer_cast<RawConfig>(
304 RawConfigPrivate::getRawConfigHelper<const RawConfig>(*this, path,
305 dummy));
306 }
307
get(const std::string & path) const308 std::shared_ptr<const RawConfig> RawConfig::get(const std::string &path) const {
309 auto dummy = [](const RawConfig &, const std::string &) {};
310 return RawConfigPrivate::getRawConfigHelper(*this, path, dummy);
311 }
312
remove(const std::string & path)313 bool RawConfig::remove(const std::string &path) {
314 auto pos = path.rfind('/');
315 auto *root = this;
316 if (pos == 0 || pos + 1 == path.size()) {
317 return false;
318 }
319
320 if (pos != std::string::npos) {
321 root = get(path.substr(0, pos)).get();
322 }
323 return root->d_func()->subItems_.erase(path.substr(pos + 1)) > 0;
324 }
325
removeAll()326 void RawConfig::removeAll() {
327 FCITX_D();
328 d->subItems_.clear();
329 }
330
setValue(std::string value)331 void RawConfig::setValue(std::string value) {
332 FCITX_D();
333 d->value_ = std::move(value);
334 }
335
setComment(std::string comment)336 void RawConfig::setComment(std::string comment) {
337 FCITX_D();
338 d->comment_ = std::move(comment);
339 }
340
setLineNumber(unsigned int lineNumber)341 void RawConfig::setLineNumber(unsigned int lineNumber) {
342 FCITX_D();
343 d->lineNumber_ = lineNumber;
344 }
345
name() const346 const std::string &RawConfig::name() const {
347 FCITX_D();
348 return d->name_;
349 }
350
comment() const351 const std::string &RawConfig::comment() const {
352 FCITX_D();
353 return d->comment_;
354 }
355
value() const356 const std::string &RawConfig::value() const {
357 FCITX_D();
358 return d->value_;
359 }
360
lineNumber() const361 unsigned int RawConfig::lineNumber() const {
362 FCITX_D();
363 return d->lineNumber_;
364 }
365
hasSubItems() const366 bool RawConfig::hasSubItems() const {
367 FCITX_D();
368 return !d->subItems_.empty();
369 }
370
subItemsSize() const371 size_t RawConfig::subItemsSize() const {
372 FCITX_D();
373 return d->subItems_.size();
374 }
375
subItems() const376 std::vector<std::string> RawConfig::subItems() const {
377 FCITX_D();
378 std::vector<std::string> result;
379 result.reserve(d->subItems_.size());
380 for (const auto &pair : d->subItems_) {
381 result.push_back(pair.first);
382 }
383 return result;
384 }
385
parent() const386 RawConfig *RawConfig::parent() const {
387 FCITX_D();
388 return d->parent_;
389 }
390
detach()391 std::shared_ptr<RawConfig> RawConfig::detach() {
392 FCITX_D();
393 if (!d->parent_) {
394 return {};
395 }
396 auto ref = d->parent_->get(d->name_);
397 d->parent_->d_func()->subItems_.erase(d->name_);
398 d->parent_ = nullptr;
399 return ref;
400 }
401
visitSubItems(std::function<bool (RawConfig &,const std::string & path)> callback,const std::string & path,bool recursive,const std::string & pathPrefix)402 bool RawConfig::visitSubItems(
403 std::function<bool(RawConfig &, const std::string &path)> callback,
404 const std::string &path, bool recursive, const std::string &pathPrefix) {
405 auto *root = this;
406 std::shared_ptr<RawConfig> subItem;
407 if (!path.empty()) {
408 subItem = get(path);
409 root = subItem.get();
410 }
411
412 if (!root) {
413 return true;
414 }
415
416 return RawConfigPrivate::visitHelper(*root, std::move(callback), recursive,
417 pathPrefix);
418 }
419
visitSubItems(std::function<bool (const RawConfig &,const std::string & path)> callback,const std::string & path,bool recursive,const std::string & pathPrefix) const420 bool RawConfig::visitSubItems(
421 std::function<bool(const RawConfig &, const std::string &path)> callback,
422 const std::string &path, bool recursive,
423 const std::string &pathPrefix) const {
424 const auto *root = this;
425 std::shared_ptr<const RawConfig> subItem;
426 if (!path.empty()) {
427 subItem = get(path);
428 root = subItem.get();
429 }
430
431 if (!root) {
432 return true;
433 }
434
435 return RawConfigPrivate::visitHelper(*root, std::move(callback), recursive,
436 pathPrefix);
437 }
438
visitItemsOnPath(std::function<void (RawConfig &,const std::string & path)> callback,const std::string & path)439 void RawConfig::visitItemsOnPath(
440 std::function<void(RawConfig &, const std::string &path)> callback,
441 const std::string &path) {
442 RawConfigPrivate::getRawConfigHelper(*this, path, std::move(callback));
443 }
visitItemsOnPath(std::function<void (const RawConfig &,const std::string & path)> callback,const std::string & path) const444 void RawConfig::visitItemsOnPath(
445 std::function<void(const RawConfig &, const std::string &path)> callback,
446 const std::string &path) const {
447 RawConfigPrivate::getRawConfigHelper(*this, path, std::move(callback));
448 }
449
createSub(std::string name)450 std::shared_ptr<RawConfig> RawConfig::createSub(std::string name) {
451 struct RawSubConfig : public RawConfig {
452 RawSubConfig(RawConfig *parent, std::string name)
453 : RawConfig(std::move(name)) {
454 FCITX_D();
455 d->parent_ = parent;
456 }
457 };
458 return std::make_shared<RawSubConfig>(this, std::move(name));
459 }
460
operator <<(LogMessageBuilder & log,const RawConfig & config)461 LogMessageBuilder &operator<<(LogMessageBuilder &log, const RawConfig &config) {
462 log << "RawConfig(=" << config.value();
463 config.visitSubItems(
464 [&log](const RawConfig &subConfig, const std::string &path) {
465 log << ", " << path << "=" << subConfig.value();
466 return true;
467 },
468 "", true);
469 log << ")";
470 return log;
471 }
472 } // namespace fcitx
473