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 <cassert>
10 #include <exception>
11 #include <list>
12 #include <memory>
13 #include <stdexcept>
14 #include <unordered_map>
15 #include "fcitx-utils/standardpath.h"
16
17 namespace fcitx {
18 class ConfigurationPrivate {
19 public:
20 std::list<std::string> optionsOrder_;
21 std::unordered_map<std::string, OptionBase *> options_;
22 };
23
Configuration()24 Configuration::Configuration()
25 : d_ptr(std::make_unique<ConfigurationPrivate>()) {}
26
~Configuration()27 Configuration::~Configuration() {}
28
dumpDescription(RawConfig & config) const29 void Configuration::dumpDescription(RawConfig &config) const {
30 return dumpDescriptionImpl(config, {});
31 }
32
dumpDescriptionImpl(RawConfig & config,const std::vector<std::string> & parentPaths) const33 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 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", 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
compareHelper(const Configuration & other) const80 bool Configuration::compareHelper(const Configuration &other) const {
81 FCITX_D();
82 for (const auto &path : d->optionsOrder_) {
83 auto optionIter = d->options_.find(path);
84 assert(optionIter != d->options_.end());
85 auto otherOptionIter = other.d_func()->options_.find(path);
86 if (*optionIter->second != *otherOptionIter->second) {
87 return false;
88 }
89 }
90 return true;
91 }
92
copyHelper(const Configuration & other)93 void Configuration::copyHelper(const Configuration &other) {
94 FCITX_D();
95 for (const auto &path : d->optionsOrder_) {
96 auto optionIter = d->options_.find(path);
97 assert(optionIter != d->options_.end());
98 auto otherOptionIter = other.d_func()->options_.find(path);
99 assert(otherOptionIter != other.d_func()->options_.end());
100 optionIter->second->copyFrom(*otherOptionIter->second);
101 }
102 }
103
load(const RawConfig & config,bool partial)104 void Configuration::load(const RawConfig &config, bool partial) {
105 FCITX_D();
106 for (const auto &path : d->optionsOrder_) {
107 auto subConfigPtr = config.get(path);
108 auto *option = d->options_[path];
109 if (!subConfigPtr) {
110 if (!partial) {
111 option->reset();
112 }
113 continue;
114 }
115 if (!option->unmarshall(*subConfigPtr, partial)) {
116 option->reset();
117 }
118 }
119 }
120
save(RawConfig & config) const121 void Configuration::save(RawConfig &config) const {
122 FCITX_D();
123 for (const auto &path : d->optionsOrder_) {
124 auto iter = d->options_.find(path);
125 assert(iter != d->options_.end());
126 if (iter->second->skipSave()) {
127 continue;
128 }
129 auto subConfigPtr = config.get(path, true);
130 iter->second->marshall(*subConfigPtr);
131 subConfigPtr->setComment(iter->second->description());
132 }
133 }
134
addOption(OptionBase * option)135 void Configuration::addOption(OptionBase *option) {
136 FCITX_D();
137 if (d->options_.count(option->path())) {
138 throw std::logic_error("Duplicate option path");
139 }
140
141 d->optionsOrder_.push_back(option->path());
142 d->options_[option->path()] = option;
143 }
144
syncDefaultValueToCurrent()145 void Configuration::syncDefaultValueToCurrent() {
146 FCITX_D();
147 for (const auto &path : d->optionsOrder_) {
148 auto iter = d->options_.find(path);
149 assert(iter != d->options_.end());
150 if (auto optionV2 = dynamic_cast<OptionBaseV2 *>(iter->second)) {
151 optionV2->syncDefaultValueToCurrent();
152 }
153 }
154 }
155
156 } // namespace fcitx
157