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