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 <unordered_map>
16 
17 namespace fcitx {
19 public:
20  std::list<std::string> optionsOrder_;
21  std::unordered_map<std::string, OptionBase *> options_;
22 };
23 
24 Configuration::Configuration()
25  : d_ptr(std::make_unique<ConfigurationPrivate>()) {}
26 
27 Configuration::~Configuration() = default;
28 
29 void Configuration::dumpDescription(RawConfig &config) const {
30  return dumpDescriptionImpl(config, {});
31 }
32 
33 void Configuration::dumpDescriptionImpl(
34  RawConfig &config, const std::vector<std::string> &parentPaths) const {
35  FCITX_D();
36  auto fullpaths = parentPaths;
37  fullpaths.push_back(typeName());
38  auto pathString = stringutils::join(fullpaths, '$');
39  std::shared_ptr<RawConfig> subRoot = config.get(pathString, true);
40  std::vector<
41  std::tuple<std::vector<std::string>, std::unique_ptr<Configuration>>>
42  subConfigs;
43  for (const auto &path : d->optionsOrder_) {
44  auto optionIter = d->options_.find(path);
45  assert(optionIter != d->options_.end());
46  auto *option = optionIter->second;
47  if (option->skipDescription()) {
48  continue;
49  }
50  auto descConfigPtr = subRoot->get(option->path(), true);
51  option->dumpDescription(*descConfigPtr);
52 
53  auto subConfig = option->subConfigSkeleton();
54  if (subConfig) {
55  auto subConfigPath = parentPaths;
56  subConfigPath.push_back(option->path());
57  std::string subTypeName = subConfig->typeName();
58  const auto *oldTypeName = descConfigPtr->valueByPath("Type");
59  // Replace the "Type" with the full name we want.
60  // Path$To$Value$TypeName
61  if (oldTypeName &&
62  stringutils::endsWith(*oldTypeName, subTypeName)) {
63  auto newTypeName = oldTypeName->substr(
64  0, oldTypeName->size() - subTypeName.size());
65  newTypeName.append(stringutils::join(subConfigPath, '$'));
66  newTypeName.append("$");
67  newTypeName.append(subTypeName);
68  descConfigPtr->setValueByPath("Type", std::move(newTypeName));
69  }
70  subConfigs.emplace_back(subConfigPath, std::move(subConfig));
71  }
72  }
73 
74  // Make sure sub type use an unique name, named after the path to the value.
75  for (const auto &[subConfigPath, subConfigPtr] : subConfigs) {
76  subConfigPtr->dumpDescriptionImpl(config, subConfigPath);
77  }
78 }
79 
80 bool Configuration::compareHelper(const Configuration &other) const {
81  FCITX_D();
82  return std::all_of(
83  d->optionsOrder_.begin(), d->optionsOrder_.end(),
84  [d, &other](const auto &path) {
85  auto optionIter = d->options_.find(path);
86  assert(optionIter != d->options_.end());
87  auto otherOptionIter = other.d_func()->options_.find(path);
88  assert(otherOptionIter != other.d_func()->options_.end());
89  return *optionIter->second == *otherOptionIter->second;
90  });
91  return true;
92 }
93 
94 void Configuration::copyHelper(const Configuration &other) {
95  FCITX_D();
96  for (const auto &path : d->optionsOrder_) {
97  auto optionIter = d->options_.find(path);
98  assert(optionIter != d->options_.end());
99  auto otherOptionIter = other.d_func()->options_.find(path);
100  assert(otherOptionIter != other.d_func()->options_.end());
101  optionIter->second->copyFrom(*otherOptionIter->second);
102  }
103 }
104 
105 void Configuration::load(const RawConfig &config, bool partial) {
106  FCITX_D();
107  for (const auto &path : d->optionsOrder_) {
108  auto subConfigPtr = config.get(path);
109  auto *option = d->options_[path];
110  if (!subConfigPtr) {
111  if (!partial) {
112  option->reset();
113  }
114  continue;
115  }
116  if (!option->unmarshall(*subConfigPtr, partial)) {
117  option->reset();
118  }
119  }
120 }
121 
122 void Configuration::save(RawConfig &config) const {
123  FCITX_D();
124  for (const auto &path : d->optionsOrder_) {
125  auto iter = d->options_.find(path);
126  assert(iter != d->options_.end());
127  if (iter->second->skipSave()) {
128  continue;
129  }
130  auto subConfigPtr = config.get(path, true);
131  iter->second->marshall(*subConfigPtr);
132  subConfigPtr->setComment(iter->second->description());
133  }
134 }
135 
136 void Configuration::addOption(OptionBase *option) {
137  FCITX_D();
138  if (d->options_.count(option->path())) {
139  throw std::logic_error("Duplicate option path");
140  }
141 
142  d->optionsOrder_.push_back(option->path());
143  d->options_[option->path()] = option;
144 }
145 
147  FCITX_D();
148  for (const auto &path : d->optionsOrder_) {
149  auto iter = d->options_.find(path);
150  assert(iter != d->options_.end());
151  // Unfortunately on certain system OptionBaseV2 doesn't have key
152  // function emit type info, so we have to add OptionBaseV3 with a
153  // non-abstract virtual funciton.
154  if (auto *optionV3 = dynamic_cast<OptionBaseV3 *>(iter->second)) {
155  optionV3->syncDefaultValueToCurrent();
156  } else if (auto *optionV2 =
157  dynamic_cast<OptionBaseV2 *>(iter->second)) {
158  optionV2->syncDefaultValueToCurrent();
159  }
160  }
161 }
162 
163 } // namespace fcitx
bool endsWith(std::string_view str, std::string_view suffix)
Check if a string ends with a suffix.
Definition: stringutils.cpp:99
Definition: action.cpp:17
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:108