Fcitx
rawconfig.cpp
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 <cstddef>
9 #include <functional>
10 #include <memory>
11 #include <string>
12 #include <utility>
13 #include <vector>
14 #include "fcitx-utils/log.h"
15 #include "fcitx-utils/macros.h"
16 #include "fcitx-utils/misc_p.h"
17 
18 namespace fcitx {
19 
20 class RawConfigPrivate : public QPtrHolder<RawConfig> {
21 public:
22  RawConfigPrivate(RawConfig *q, std::string _name)
23  : QPtrHolder(q), name_(std::move(_name)), lineNumber_(0),
24  implicit_(false) {}
26  : QPtrHolder(q), value_(other.value_), comment_(other.comment_),
27  lineNumber_(other.lineNumber_), implicit_(other.implicit_) {}
28 
29  RawConfigPrivate &operator=(const RawConfigPrivate &other) {
30  if (&other == this) {
31  return *this;
32  }
33  // There is no need to copy "name_", because name refers to its parent.
34  // Make a copy of value, comment, lineNumber, and implicit_, because
35  // this "other" might be our parent.
36  auto value = other.value_;
37  auto comment = other.comment_;
38  auto lineNumber = other.lineNumber_;
39  auto implicit = other.implicit_;
40  OrderedMap<std::string, std::shared_ptr<RawConfig>> newSubItems;
41  for (const auto &item : other.subItems_) {
42  auto result = newSubItems[item.first] =
43  q_func()->createSub(item.second->name());
44  *result = *item.second;
45  }
46  value_ = std::move(value);
47  comment_ = std::move(comment);
48  lineNumber_ = lineNumber;
49  implicit_ = implicit;
50  detachSubItems();
51  subItems_ = std::move(newSubItems);
52  return *this;
53  }
54 
55  std::shared_ptr<RawConfig> getNonexistentRawConfig(RawConfig *q,
56  const std::string &key) {
57  auto result = subItems_[key] = q->createSub(key);
58  return result;
59  }
60 
61  static std::shared_ptr<const RawConfig>
62  getNonexistentRawConfig(const RawConfig * /*unused*/,
63  const std::string & /*unused*/) {
64  return nullptr;
65  }
66 
67  template <typename T, typename U>
68  static std::shared_ptr<T>
69  getRawConfigHelper(T &that, const std::string &path, U callback) {
70  auto *cur = &that;
71  std::shared_ptr<T> result;
72  for (std::string::size_type pos = 0, new_pos = path.find('/', pos);
73  pos != std::string::npos && cur;
74  pos = ((std::string::npos == new_pos) ? new_pos : (new_pos + 1)),
75  new_pos = path.find('/', pos)) {
76  auto key = path.substr(pos, (std::string::npos == new_pos)
77  ? new_pos
78  : (new_pos - pos));
79  auto iter = cur->d_func()->subItems_.find(key);
80  if (iter == cur->d_func()->subItems_.end()) {
81  result = cur->d_func()->getNonexistentRawConfig(cur, key);
82  } else {
83  result = iter->second;
84  }
85  cur = result.get();
86 
87  if (cur) {
88  callback(*cur, path.substr(0, new_pos));
89  }
90  }
91  return result;
92  }
93 
94  template <typename T>
95  static bool
96  visitHelper(T &that,
97  std::function<bool(T &, const std::string &path)> callback,
98  bool recursive, const std::string &pathPrefix) {
99  auto d = that.d_func();
100  for (const auto &pair : d->subItems_) {
101  std::shared_ptr<T> item = pair.second;
102  auto newPathPrefix = pathPrefix.empty()
103  ? item->name()
104  : pathPrefix + "/" + item->name();
105  if (!callback(*item, newPathPrefix)) {
106  return false;
107  }
108  if (recursive) {
109  if (!visitHelper(*item, callback, recursive, newPathPrefix)) {
110  return false;
111  }
112  }
113  }
114  return true;
115  }
116 
117  void detachSubItems() {
118  for (const auto &pair : subItems_) {
119  pair.second->d_func()->parent_ = nullptr;
120  }
121  }
122 
123  RawConfig *parent_ = nullptr;
124  const std::string name_;
125  std::string value_;
126  std::string comment_;
127  OrderedMap<std::string, std::shared_ptr<RawConfig>> subItems_;
128  unsigned int lineNumber_;
129  bool implicit_;
130 };
131 
133 
134 RawConfig::RawConfig(std::string name)
135  : d_ptr(std::make_unique<RawConfigPrivate>(this, std::move(name))) {}
136 
137 RawConfig::~RawConfig() {
138  FCITX_D();
139  d->detachSubItems();
140 }
141 
142 RawConfig::RawConfig(const RawConfig &other)
143  : d_ptr(std::make_unique<RawConfigPrivate>(this, *other.d_ptr)) {
144  for (const auto &item : other.d_func()->subItems_) {
145  *get(item.first, true) = *item.second;
146  }
147 }
149  *d_ptr = *other.d_ptr;
150  return *this;
151 }
152 
153 std::shared_ptr<RawConfig> RawConfig::get(const std::string &path,
154  bool create) {
155  auto dummy = [](const RawConfig &, const std::string &) {};
156  if (create) {
157  return RawConfigPrivate::getRawConfigHelper(*this, path, dummy);
158  }
159  return std::const_pointer_cast<RawConfig>(
160  RawConfigPrivate::getRawConfigHelper<const RawConfig>(*this, path,
161  dummy));
162 }
163 
164 std::shared_ptr<const RawConfig> RawConfig::get(const std::string &path) const {
165  auto dummy = [](const RawConfig &, const std::string &) {};
166  return RawConfigPrivate::getRawConfigHelper(*this, path, dummy);
167 }
168 
169 bool RawConfig::remove(const std::string &path) {
170  auto pos = path.rfind('/');
171  auto *root = this;
172  if (pos == 0 || pos + 1 == path.size()) {
173  return false;
174  }
175 
176  if (pos != std::string::npos) {
177  root = get(path.substr(0, pos)).get();
178  }
179  return root->d_func()->subItems_.erase(path.substr(pos + 1)) > 0;
180 }
181 
183  FCITX_D();
184  d->subItems_.clear();
185 }
186 
187 void RawConfig::setValue(std::string value) {
188  FCITX_D();
189  d->value_ = std::move(value);
190 }
191 
192 void RawConfig::setComment(std::string comment) {
193  FCITX_D();
194  d->comment_ = std::move(comment);
195 }
196 
198  FCITX_D();
199  d->lineNumber_ = lineNumber;
200 }
201 
202 void RawConfig::setImplicit(bool implicit) {
203  FCITX_D();
204  d->implicit_ = implicit;
205 }
206 
207 const std::string &RawConfig::name() const {
208  FCITX_D();
209  return d->name_;
210 }
211 
212 const std::string &RawConfig::comment() const {
213  FCITX_D();
214  return d->comment_;
215 }
216 
217 const std::string &RawConfig::value() const {
218  FCITX_D();
219  return d->value_;
220 }
221 
222 unsigned int RawConfig::lineNumber() const {
223  FCITX_D();
224  return d->lineNumber_;
225 }
226 
228  FCITX_D();
229  return d->implicit_;
230 }
231 
232 bool RawConfig::isImplicit() const {
233  const RawConfig *node = this;
234  while (node) {
235  if (node->isImplicitSelf()) {
236  return true;
237  }
238  node = node->parent();
239  }
240  return false;
241 }
242 
244  FCITX_D();
245  return !d->subItems_.empty();
246 }
247 
248 size_t RawConfig::subItemsSize() const {
249  FCITX_D();
250  return d->subItems_.size();
251 }
252 
253 std::vector<std::string> RawConfig::subItems() const {
254  FCITX_D();
255  std::vector<std::string> result;
256  result.reserve(d->subItems_.size());
257  for (const auto &pair : d->subItems_) {
258  result.push_back(pair.first);
259  }
260  return result;
261 }
262 
264  FCITX_D();
265  return d->parent_;
266 }
267 
268 std::shared_ptr<RawConfig> RawConfig::detach() {
269  FCITX_D();
270  if (!d->parent_) {
271  return {};
272  }
273  auto ref = d->parent_->get(d->name_);
274  d->parent_->d_func()->subItems_.erase(d->name_);
275  d->parent_ = nullptr;
276  return ref;
277 }
278 
280  std::function<bool(RawConfig &, const std::string &path)> callback,
281  const std::string &path, bool recursive, const std::string &pathPrefix) {
282  auto *root = this;
283  std::shared_ptr<RawConfig> subItem;
284  if (!path.empty()) {
285  subItem = get(path);
286  root = subItem.get();
287  }
288 
289  if (!root) {
290  return true;
291  }
292 
293  return RawConfigPrivate::visitHelper(*root, std::move(callback), recursive,
294  pathPrefix);
295 }
296 
298  std::function<bool(const RawConfig &, const std::string &path)> callback,
299  const std::string &path, bool recursive,
300  const std::string &pathPrefix) const {
301  const auto *root = this;
302  std::shared_ptr<const RawConfig> subItem;
303  if (!path.empty()) {
304  subItem = get(path);
305  root = subItem.get();
306  }
307 
308  if (!root) {
309  return true;
310  }
311 
312  return RawConfigPrivate::visitHelper(*root, std::move(callback), recursive,
313  pathPrefix);
314 }
315 
317  std::function<void(RawConfig &, const std::string &path)> callback,
318  const std::string &path) {
319  RawConfigPrivate::getRawConfigHelper(*this, path, std::move(callback));
320 }
322  std::function<void(const RawConfig &, const std::string &path)> callback,
323  const std::string &path) const {
324  RawConfigPrivate::getRawConfigHelper(*this, path, std::move(callback));
325 }
326 
327 std::shared_ptr<RawConfig> RawConfig::createSub(std::string name) {
328  struct RawSubConfig : public RawConfig {
329  RawSubConfig(RawConfig *parent, std::string name)
330  : RawConfig(std::move(name)) {
331  FCITX_D();
332  d->parent_ = parent;
333  }
334  };
335  return std::make_shared<RawSubConfig>(this, std::move(name));
336 }
337 
338 LogMessageBuilder &operator<<(LogMessageBuilder &log, const RawConfig &config) {
339  log << "RawConfig(=" << config.value();
340  config.visitSubItems(
341  [&log](const RawConfig &subConfig, const std::string &path) {
342  log << ", " << path << "=" << subConfig.value();
343  return true;
344  },
345  "", true);
346  log << ")";
347  return log;
348 }
349 } // namespace fcitx
RawConfig & operator=(std::string value)
Assign a string value to this node.
Definition: rawconfig.h:184
RawConfig()
Construct a RawConfig with an empty name.
Definition: rawconfig.cpp:132
void setLineNumber(unsigned int lineNumber)
Set the line number where this node was parsed from.
Definition: rawconfig.cpp:197
const std::string & name() const
Get the name of this node.
Definition: rawconfig.cpp:207
const std::string & comment() const
Get the comment for this node.
Definition: rawconfig.cpp:212
bool hasSubItems() const
Check if this node has any sub-items.
Definition: rawconfig.cpp:243
void setComment(std::string comment)
Set the comment for this node.
Definition: rawconfig.cpp:192
A raw configuration tree that stores key-value pairs in a hierarchical structure. ...
Definition: rawconfig.h:37
unsigned int lineNumber() const
Get the line number where this node was parsed from.
Definition: rawconfig.cpp:222
Definition: action.cpp:17
std::shared_ptr< RawConfig > get(const std::string &path, bool create=false)
Get a sub-item by path.
Definition: rawconfig.cpp:153
bool isImplicit() const
Check if this node or any of its ancestors is marked as implicit.
Definition: rawconfig.cpp:232
void removeAll()
Remove all sub-items.
Definition: rawconfig.cpp:182
std::vector< std::string > subItems() const
Get a list of sub-item names.
Definition: rawconfig.cpp:253
void visitItemsOnPath(std::function< void(RawConfig &, const std::string &path)> callback, const std::string &path)
Visit all items on a path.
Definition: rawconfig.cpp:316
bool isImplicitSelf() const
Check if this node itself is marked as implicit.
Definition: rawconfig.cpp:227
void setImplicit(bool implicit)
Mark this config node as implicit (default value that won&#39;t be written to INI).
Definition: rawconfig.cpp:202
bool visitSubItems(std::function< bool(RawConfig &, const std::string &path)> callback, const std::string &path="", bool recursive=false, const std::string &pathPrefix="")
Visit all sub-items.
Definition: rawconfig.cpp:279
size_t subItemsSize() const
Get the number of sub-items.
Definition: rawconfig.cpp:248
RawConfig * parent() const
Get the parent node.
Definition: rawconfig.cpp:263
void setValue(std::string value)
Set the value of this node.
Definition: rawconfig.cpp:187
const std::string & value() const
Get the value of this node.
Definition: rawconfig.cpp:217
bool remove(const std::string &path)
Remove a sub-item by path.
Definition: rawconfig.cpp:169
std::shared_ptr< RawConfig > detach()
Detach this node from its parent.
Definition: rawconfig.cpp:268
Log utilities.