Fcitx
configuration.cpp
1 /*
2  * SPDX-FileCopyrightText: 2015-2015 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "configuration.h"
9 #include <algorithm>
10 #include <cassert>
11 #include <list>
12 #include <memory>
13 #include <stdexcept>
14 #include <string>
15 #include <tuple>
16 #include <unordered_map>
17 #include <utility>
18 #include <vector>
19 #include "fcitx-utils/macros.h"
21 #include "option.h"
22 #include "rawconfig.h"
23 
24 namespace fcitx {
26 public:
27  std::list<std::string> optionsOrder_;
28  std::unordered_map<std::string, OptionBase *> options_;
29 };
30 
31 Configuration::Configuration()
32  : d_ptr(std::make_unique<ConfigurationPrivate>()) {}
33 
34 Configuration::~Configuration() = default;
35 
36 void Configuration::dumpDescription(RawConfig &config) const {
37  dumpDescriptionImpl(config, {});
38 }
39 
40 void Configuration::dumpDescriptionImpl(
41  RawConfig &config, const std::vector<std::string> &parentPaths) const {
42  FCITX_D();
43  auto fullpaths = parentPaths;
44  fullpaths.push_back(typeName());
45  auto pathString = stringutils::join(fullpaths, '$');
46  std::shared_ptr<RawConfig> subRoot = config.get(pathString, true);
47  std::vector<
48  std::tuple<std::vector<std::string>, std::unique_ptr<Configuration>>>
49  subConfigs;
50  for (const auto &path : d->optionsOrder_) {
51  auto optionIter = d->options_.find(path);
52  assert(optionIter != d->options_.end());
53  auto *option = optionIter->second;
54  if (option->skipDescription()) {
55  continue;
56  }
57  auto descConfigPtr = subRoot->get(option->path(), true);
58  option->dumpDescription(*descConfigPtr);
59 
60  auto subConfig = option->subConfigSkeleton();
61  if (subConfig) {
62  auto subConfigPath = parentPaths;
63  subConfigPath.push_back(option->path());
64  std::string subTypeName = subConfig->typeName();
65  const auto *oldTypeName = descConfigPtr->valueByPath("Type");
66  // Replace the "Type" with the full name we want.
67  // Path$To$Value$TypeName
68  if (oldTypeName && oldTypeName->ends_with(subTypeName)) {
69  auto newTypeName = oldTypeName->substr(
70  0, oldTypeName->size() - subTypeName.size());
71  newTypeName.append(stringutils::join(subConfigPath, '$'));
72  newTypeName.append("$");
73  newTypeName.append(subTypeName);
74  descConfigPtr->setValueByPath("Type", std::move(newTypeName));
75  }
76  subConfigs.emplace_back(subConfigPath, std::move(subConfig));
77  }
78  }
79 
80  // Make sure sub type use an unique name, named after the path to the value.
81  for (const auto &[subConfigPath, subConfigPtr] : subConfigs) {
82  subConfigPtr->dumpDescriptionImpl(config, subConfigPath);
83  }
84 }
85 
86 bool Configuration::compareHelper(const Configuration &other) const {
87  FCITX_D();
88  return std::ranges::all_of(d->optionsOrder_, [d, &other](const auto &path) {
89  auto optionIter = d->options_.find(path);
90  assert(optionIter != d->options_.end());
91  auto otherOptionIter = other.d_func()->options_.find(path);
92  assert(otherOptionIter != other.d_func()->options_.end());
93  return *optionIter->second == *otherOptionIter->second;
94  });
95  return true;
96 }
97 
98 void Configuration::copyHelper(const Configuration &other) {
99  FCITX_D();
100  for (const auto &path : d->optionsOrder_) {
101  auto optionIter = d->options_.find(path);
102  assert(optionIter != d->options_.end());
103  auto otherOptionIter = other.d_func()->options_.find(path);
104  assert(otherOptionIter != other.d_func()->options_.end());
105  optionIter->second->copyFrom(*otherOptionIter->second);
106  }
107 }
108 
109 void Configuration::load(const RawConfig &config, bool partial) {
110  FCITX_D();
111  for (const auto &path : d->optionsOrder_) {
112  auto subConfigPtr = config.get(path);
113  auto *option = d->options_[path];
114  if (!subConfigPtr) {
115  if (!partial) {
116  option->reset();
117  }
118  continue;
119  }
120  if (!option->unmarshall(*subConfigPtr, partial)) {
121  option->reset();
122  }
123  }
124 }
125 
126 void Configuration::save(RawConfig &config) const {
127  FCITX_D();
128  for (const auto &path : d->optionsOrder_) {
129  auto iter = d->options_.find(path);
130  assert(iter != d->options_.end());
131  if (iter->second->skipSave()) {
132  continue;
133  }
134  auto subConfigPtr = config.get(path, true);
135  iter->second->marshall(*subConfigPtr);
136  subConfigPtr->setComment(iter->second->description());
137  subConfigPtr->setImplicit(iter->second->isDefault());
138  }
139 }
140 
141 void Configuration::addOption(OptionBase *option) {
142  FCITX_D();
143  if (d->options_.contains(option->path())) {
144  throw std::logic_error("Duplicate option path");
145  }
146 
147  d->optionsOrder_.push_back(option->path());
148  d->options_[option->path()] = option;
149 }
150 
152  FCITX_D();
153  for (const auto &path : d->optionsOrder_) {
154  auto iter = d->options_.find(path);
155  assert(iter != d->options_.end());
156  // Unfortunately on certain system OptionBaseV2 doesn't have key
157  // function emit type info, so we have to add OptionBaseV3 with a
158  // non-abstract virtual function.
159  if (auto *optionV3 = dynamic_cast<OptionBaseV3 *>(iter->second)) {
160  optionV3->syncDefaultValueToCurrent();
161  } else if (auto *optionV2 =
162  dynamic_cast<OptionBaseV2 *>(iter->second)) {
163  optionV2->syncDefaultValueToCurrent();
164  }
165  }
166 }
167 
168 } // namespace fcitx
A raw configuration tree that stores key-value pairs in a hierarchical structure. ...
Definition: rawconfig.h:37
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
void load(const RawConfig &config, bool partial=false)
Load configuration from RawConfig.
void syncDefaultValueToCurrent()
Set default value to current value.
String handle utilities.
std::string join(Iter start, Iter end, T &&delim)
Join a range of string with delim.
Definition: stringutils.h:109