2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements functions from treesupport.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_options
44 #include "treesupport.h"
50 #include "gromacs/options/options.h"
51 #include "gromacs/options/optionsassigner.h"
52 #include "gromacs/options/optionsection.h"
53 #include "gromacs/options/optionsvisitor.h"
54 #include "gromacs/utility/exceptions.h"
55 #include "gromacs/utility/ikeyvaluetreeerror.h"
56 #include "gromacs/utility/keyvaluetree.h"
57 #include "gromacs/utility/keyvaluetreebuilder.h"
58 #include "gromacs/utility/stringutil.h"
66 class TreeAssignHelper
69 TreeAssignHelper(Options* options, IKeyValueTreeErrorHandler* errorHandler) :
71 errorHandler_(errorHandler)
73 if (errorHandler_ == nullptr)
75 errorHandler_ = defaultKeyValueTreeErrorHandler();
79 void assignAll(const KeyValueTreeObject& root)
87 void assignSubTree(const KeyValueTreeObject& tree)
89 // TODO: Use the error handler also in other case.
90 for (const KeyValueTreeProperty& prop : tree.properties())
92 context_.append(prop.key());
93 if (prop.value().isArray())
95 assignArray(prop.key(), prop.value().asArray());
97 else if (prop.value().isObject())
99 assigner_.startSection(prop.key().c_str());
100 assignSubTree(prop.value().asObject());
101 assigner_.finishSection();
105 assigner_.startOption(prop.key().c_str());
108 assigner_.appendValue(prop.value().asAny());
110 catch (UserInputError& ex)
112 if (!errorHandler_->onError(&ex, context_))
117 assigner_.finishOption();
123 void assignArray(const std::string& key, const KeyValueTreeArray& array)
125 if (array.isObjectArray())
127 for (const KeyValueTreeValue& value : array.values())
129 assigner_.startSection(key.c_str());
130 assignSubTree(value.asObject());
131 assigner_.finishSection();
136 assigner_.startOption(key.c_str());
137 for (const KeyValueTreeValue& value : array.values())
139 assigner_.appendValue(value.asAny());
141 assigner_.finishOption();
145 OptionsAssigner assigner_;
146 IKeyValueTreeErrorHandler* errorHandler_;
147 KeyValueTreePath context_;
150 class TreeCheckHelper : private OptionsVisitor
153 TreeCheckHelper(const KeyValueTreeObject& root) :
154 currentObject_(&root),
155 currentKnownNames_(nullptr)
159 bool hasUnknownPaths() const { return !unknownPaths_.empty(); }
160 const std::vector<KeyValueTreePath>& unknownPaths() const { return unknownPaths_; }
162 void processOptionSection(const OptionSectionInfo& section)
164 OptionsIterator iterator(section);
165 std::set<std::string> knownNames;
166 currentKnownNames_ = &knownNames;
167 iterator.acceptOptions(this);
168 iterator.acceptSections(this);
169 currentKnownNames_ = nullptr;
170 for (const auto& prop : currentObject_->properties())
172 if (knownNames.count(prop.key()) == 0)
174 unknownPaths_.push_back(currentPath_ + prop.key());
180 void visitSection(const OptionSectionInfo& section) override
182 const std::string& name = section.name();
183 if (currentObject_->keyExists(name))
185 currentKnownNames_->insert(name);
186 auto parentObject = currentObject_;
187 auto parentKnownNames = currentKnownNames_;
188 // TODO: Consider what to do with mismatching types.
189 currentObject_ = &(*currentObject_)[name].asObject();
190 currentPath_.append(name);
191 processOptionSection(section);
192 currentPath_.pop_back();
193 currentObject_ = parentObject;
194 currentKnownNames_ = parentKnownNames;
197 void visitOption(const OptionInfo& option) override
199 const std::string& name = option.name();
200 if (currentObject_->keyExists(name))
202 currentKnownNames_->insert(name);
203 // TODO: Consider what to do with mismatching types.
207 KeyValueTreePath currentPath_;
208 const KeyValueTreeObject* currentObject_;
209 std::set<std::string>* currentKnownNames_;
210 std::vector<KeyValueTreePath> unknownPaths_;
213 class TreeAdjustHelper : private OptionsVisitor
216 TreeAdjustHelper(const KeyValueTreeObject& root, KeyValueTreeBuilder* builder) :
217 currentSourceObject_(&root),
218 currentObjectBuilder_(builder->rootObject())
222 void processOptionSection(const OptionSectionInfo& section)
224 OptionsIterator iterator(section);
225 iterator.acceptOptions(this);
226 iterator.acceptSections(this);
230 void visitSection(const OptionSectionInfo& section) override
232 const std::string& name = section.name();
233 auto parentBuilder = currentObjectBuilder_;
234 auto parentObject = currentSourceObject_;
235 currentObjectBuilder_ = currentObjectBuilder_.addObject(name);
236 currentSourceObject_ = (currentSourceObject_ != nullptr && currentSourceObject_->keyExists(name)
237 ? &(*currentSourceObject_)[name].asObject()
239 processOptionSection(section);
240 currentSourceObject_ = parentObject;
241 currentObjectBuilder_ = parentBuilder;
243 void visitOption(const OptionInfo& option) override
245 const std::string& name = option.name();
246 if (currentSourceObject_ == nullptr || !currentSourceObject_->keyExists(name))
248 std::vector<Any> values = option.defaultValues();
249 if (values.size() == 1)
251 currentObjectBuilder_.addRawValue(name, std::move(values[0]));
253 else if (values.size() > 1)
255 auto arrayBuilder = currentObjectBuilder_.addArray(name);
256 for (Any& value : values)
258 arrayBuilder.addRawValue(std::move(value));
264 const KeyValueTreeValue& value = (*currentSourceObject_)[name];
265 GMX_RELEASE_ASSERT(!value.isObject(), "Value objects not supported in this context");
266 std::vector<Any> values;
269 for (const auto& arrayValue : value.asArray().values())
271 GMX_RELEASE_ASSERT(!value.isObject() && !value.isArray(),
272 "Complex values not supported in this context");
273 values.push_back(arrayValue.asAny());
278 values.push_back(value.asAny());
280 values = option.normalizeValues(values);
281 if (values.empty()) {}
282 else if (values.size() == 1)
284 currentObjectBuilder_.addRawValue(name, std::move(values[0]));
288 auto array = currentObjectBuilder_.addArray(name);
289 for (auto& arrayValue : values)
291 array.addRawValue(std::move(arrayValue));
297 const KeyValueTreeObject* currentSourceObject_;
298 KeyValueTreeObjectBuilder currentObjectBuilder_;
305 void assignOptionsFromKeyValueTree(Options* options,
306 const KeyValueTreeObject& tree,
307 IKeyValueTreeErrorHandler* errorHandler)
309 TreeAssignHelper helper(options, errorHandler);
310 helper.assignAll(tree);
313 void checkForUnknownOptionsInKeyValueTree(const KeyValueTreeObject& tree, const Options& options)
315 TreeCheckHelper helper(tree);
316 helper.processOptionSection(options.rootSection());
317 if (helper.hasUnknownPaths())
319 std::string paths(formatAndJoin(helper.unknownPaths(), "\n ", [](const KeyValueTreePath& path) {
320 return path.toString();
322 std::string message("Unknown input values:\n " + paths);
323 GMX_THROW(InvalidInputError(message));
327 KeyValueTreeObject adjustKeyValueTreeFromOptions(const KeyValueTreeObject& tree, const Options& options)
329 KeyValueTreeBuilder builder;
330 TreeAdjustHelper helper(tree, &builder);
331 helper.processOptionSection(options.rootSection());
332 return builder.build();