Remove unused function generate_excls and make clean_excls static
[gromacs.git] / src / gromacs / utility / keyvaluetreetransform.cpp
blob343143dd0dc1fc2d504df38a44efa82d7b8f9363
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016,2017,2018,2019, 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.
35 #include "gmxpre.h"
37 #include "keyvaluetreetransform.h"
39 #include <functional>
40 #include <map>
41 #include <memory>
42 #include <typeindex>
43 #include <vector>
45 #include "gromacs/utility/exceptions.h"
46 #include "gromacs/utility/ikeyvaluetreeerror.h"
47 #include "gromacs/utility/keyvaluetreebuilder.h"
48 #include "gromacs/utility/stringcompare.h"
49 #include "gromacs/utility/stringutil.h"
51 namespace gmx
54 /********************************************************************
55 * IKeyValueTreeTransformRules
58 IKeyValueTreeTransformRules::~IKeyValueTreeTransformRules()
62 /********************************************************************
63 * KeyValueTreeTransformRulesScoped::Impl
66 class KeyValueTreeTransformRulesScoped::Impl : public IKeyValueTreeTransformRules
68 public:
69 Impl(internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
70 : impl_(impl), prefix_(prefix)
74 KeyValueTreeTransformRuleBuilder addRule() override
76 return KeyValueTreeTransformRuleBuilder(impl_, prefix_);
79 KeyValueTreeTransformRulesScoped
80 scopedTransform(const KeyValueTreePath &scope) override
82 return KeyValueTreeTransformRulesScoped(impl_, prefix_ + scope);
85 private:
86 internal::KeyValueTreeTransformerImpl *impl_;
87 KeyValueTreePath prefix_;
90 /********************************************************************
91 * KeyValueTreeTransformRulesScoped
94 KeyValueTreeTransformRulesScoped::KeyValueTreeTransformRulesScoped(
95 internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
96 : impl_(new Impl(impl, prefix))
100 KeyValueTreeTransformRulesScoped::KeyValueTreeTransformRulesScoped(
101 KeyValueTreeTransformRulesScoped &&) noexcept = default;
103 KeyValueTreeTransformRulesScoped &
104 KeyValueTreeTransformRulesScoped::operator=(
105 KeyValueTreeTransformRulesScoped &&) noexcept = default;
107 KeyValueTreeTransformRulesScoped::~KeyValueTreeTransformRulesScoped()
111 IKeyValueTreeTransformRules *KeyValueTreeTransformRulesScoped::rules()
113 return impl_.get();
116 /********************************************************************
117 * IKeyValueTreeBackMapping
120 IKeyValueTreeBackMapping::~IKeyValueTreeBackMapping()
124 namespace
127 class KeyValueTreeBackMapping : public IKeyValueTreeBackMapping
129 public:
130 class Entry
132 public:
133 Entry() = default;
134 explicit Entry(const KeyValueTreePath &path) : sourcePath_(path) {}
136 Entry *getOrCreateChildEntry(const std::string &key)
138 auto iter = childEntries_.find(key);
139 if (iter == childEntries_.end())
141 iter = childEntries_.insert(std::make_pair(key, Entry())).first;
143 return &iter->second;
145 void setMapping(const KeyValueTreePath &path,
146 const KeyValueTreeValue &value)
148 GMX_RELEASE_ASSERT(sourcePath_.empty(),
149 "Multiple entries map to same path");
150 if (value.isObject())
152 const KeyValueTreeObject &object = value.asObject();
153 for (const auto &prop : object.properties())
155 GMX_RELEASE_ASSERT(!prop.value().isObject(),
156 "Nested objects not implemented");
157 childEntries_[prop.key()] = Entry(path);
160 else
162 sourcePath_ = path;
166 KeyValueTreePath sourcePath_;
167 std::map<std::string, Entry> childEntries_;
170 KeyValueTreePath
171 originalPath(const KeyValueTreePath &path) const override
173 const Entry *entry = &rootEntry_;
174 for (const auto &element : path.elements())
176 auto iter = entry->childEntries_.find(element);
177 if (iter == entry->childEntries_.end())
179 break;
181 entry = &iter->second;
183 GMX_RELEASE_ASSERT(entry->childEntries_.empty()
184 && !entry->sourcePath_.empty(),
185 "Requested path not uniquely mapped");
186 return entry->sourcePath_;
189 Entry *rootEntry() { return &rootEntry_; }
191 private:
192 Entry rootEntry_;
195 } // namespace
197 namespace internal
200 /********************************************************************
201 * KeyValueTreeTransformerImpl
204 class KeyValueTreeTransformerImpl
206 public:
207 class Rule
209 public:
210 typedef std::function<void(KeyValueTreeValueBuilder *, const KeyValueTreeValue &)>
211 TransformFunction;
212 typedef std::map<std::string, Rule, StringCompare> ChildRuleMap;
214 explicit Rule(StringCompareType keyMatchType)
215 : expectedType_(typeid(void)), childRules_(keyMatchType)
219 const Rule *findMatchingChildRule(const std::string &key) const
221 auto iter = childRules_.find(key);
222 if (iter == childRules_.end())
224 return nullptr;
226 return &iter->second;
228 Rule *getOrCreateChildRule(const std::string &key)
230 auto iter = childRules_.find(key);
231 if (iter == childRules_.end())
233 return createChildRule(key, StringCompareType::Exact);
235 return &iter->second;
237 Rule *createChildRule(const std::string &key,
238 StringCompareType keyMatchType)
240 auto result = childRules_.insert(std::make_pair(key, Rule(keyMatchType)));
241 GMX_RELEASE_ASSERT(result.second,
242 "Cannot specify key match type after child rules");
243 return &result.first->second;
246 void collectMappedPaths(const KeyValueTreePath &prefix,
247 std::vector<KeyValueTreePath> *result) const
249 for (const auto &value : childRules_)
251 KeyValueTreePath path = prefix;
252 path.append(value.first);
253 const Rule &rule = value.second;
254 if (rule.transform_)
256 result->push_back(path);
258 else
260 rule.collectMappedPaths(path, result);
265 KeyValueTreePath targetPath_;
266 std::string targetKey_;
267 std::type_index expectedType_;
268 TransformFunction transform_;
269 ChildRuleMap childRules_;
272 class Transformer
274 public:
275 explicit Transformer(IKeyValueTreeErrorHandler *errorHandler)
276 : errorHandler_(errorHandler),
277 backMapping_(new KeyValueTreeBackMapping)
279 if (errorHandler_ == nullptr)
281 errorHandler_ = defaultKeyValueTreeErrorHandler();
285 void transform(const Rule *rootRule, const KeyValueTreeObject &tree)
287 if (rootRule != nullptr)
289 doChildTransforms(rootRule, tree);
293 KeyValueTreeTransformResult result()
295 return KeyValueTreeTransformResult(builder_.build(),
296 std::move(backMapping_));
299 private:
300 void doTransform(const Rule *rule, const KeyValueTreeValue &value);
301 void doChildTransforms(const Rule *rule, const KeyValueTreeObject &object);
302 void applyTransformedValue(const Rule *rule, KeyValueTreeValue &&value);
304 IKeyValueTreeErrorHandler *errorHandler_;
305 KeyValueTreeBuilder builder_;
306 std::unique_ptr<KeyValueTreeBackMapping> backMapping_;
307 KeyValueTreePath context_;
310 KeyValueTreeTransformerImpl()
311 : rootScope_(this, KeyValueTreePath())
315 Rule *getOrCreateRootRule()
317 if (rootRule_ == nullptr)
319 createRootRule(StringCompareType::Exact);
321 return rootRule_.get();
323 void createRootRule(StringCompareType keyMatchType)
325 GMX_RELEASE_ASSERT(rootRule_ == nullptr,
326 "Cannot specify key match type after child rules");
327 rootRule_ = std::make_unique<Rule>(keyMatchType);
330 std::unique_ptr<Rule> rootRule_;
331 KeyValueTreeTransformRulesScoped rootScope_;
334 /********************************************************************
335 * KeyValueTreeTransformerImpl::Transformer
338 void KeyValueTreeTransformerImpl::Transformer::doTransform(
339 const Rule *rule, const KeyValueTreeValue &value)
341 if (rule->transform_ != nullptr)
343 KeyValueTreeValueBuilder valueBuilder;
346 if (value.type() != rule->expectedType_)
348 // TODO: Better error message.
349 GMX_THROW(InvalidInputError("Unexpected type of value"));
351 rule->transform_(&valueBuilder, value);
353 catch (UserInputError &ex)
355 if (!errorHandler_->onError(&ex, context_))
357 throw;
359 return;
361 applyTransformedValue(rule, valueBuilder.build());
362 return;
364 if (!rule->childRules_.empty())
366 doChildTransforms(rule, value.asObject());
370 void KeyValueTreeTransformerImpl::Transformer::doChildTransforms(
371 const Rule *rule, const KeyValueTreeObject &object)
373 for (const auto &prop : object.properties())
375 const Rule *childRule = rule->findMatchingChildRule(prop.key());
376 if (childRule != nullptr)
378 context_.append(prop.key());
379 doTransform(childRule, prop.value());
380 context_.pop_back();
385 void KeyValueTreeTransformerImpl::Transformer::applyTransformedValue(
386 const Rule *rule, KeyValueTreeValue &&value)
388 KeyValueTreeObjectBuilder objBuilder = builder_.rootObject();
389 KeyValueTreeBackMapping::Entry *mapEntry = backMapping_->rootEntry();
390 for (const std::string &key : rule->targetPath_.elements())
392 if (objBuilder.keyExists(key))
394 GMX_RELEASE_ASSERT(objBuilder[key].isObject(),
395 "Inconsistent transform (different items map to same path)");
396 objBuilder = objBuilder.getObjectBuilder(key);
398 else
400 objBuilder = objBuilder.addObject(key);
402 mapEntry = mapEntry->getOrCreateChildEntry(key);
404 mapEntry = mapEntry->getOrCreateChildEntry(rule->targetKey_);
405 mapEntry->setMapping(context_, value);
406 if (objBuilder.keyExists(rule->targetKey_))
408 GMX_RELEASE_ASSERT(value.isObject(),
409 "Inconsistent transform (different items map to same path)");
410 GMX_RELEASE_ASSERT(objBuilder[rule->targetKey_].isObject(),
411 "Inconsistent transform (different items map to same path)");
412 objBuilder = objBuilder.getObjectBuilder(rule->targetKey_);
413 GMX_RELEASE_ASSERT(objBuilder.objectHasDistinctProperties(value.asObject()),
414 "Inconsistent transform (different items map to same path)");
415 objBuilder.mergeObject(std::move(value.asObject()));
417 else
419 objBuilder.addRawValue(rule->targetKey_, std::move(value));
423 } // namespace internal
425 /********************************************************************
426 * KeyValueTreeTransformer
429 KeyValueTreeTransformer::KeyValueTreeTransformer()
430 : impl_(new internal::KeyValueTreeTransformerImpl)
434 KeyValueTreeTransformer::~KeyValueTreeTransformer()
438 IKeyValueTreeTransformRules *KeyValueTreeTransformer::rules()
440 return impl_->rootScope_.rules();
443 std::vector<KeyValueTreePath> KeyValueTreeTransformer::mappedPaths() const
445 std::vector<KeyValueTreePath> result;
446 if (impl_->rootRule_)
448 impl_->rootRule_->collectMappedPaths(KeyValueTreePath(), &result);
450 return result;
453 KeyValueTreeTransformResult
454 KeyValueTreeTransformer::transform(const KeyValueTreeObject &tree,
455 IKeyValueTreeErrorHandler *errorHandler) const
457 internal::KeyValueTreeTransformerImpl::Transformer transformer(errorHandler);
458 transformer.transform(impl_->rootRule_.get(), tree);
459 return transformer.result();
462 /********************************************************************
463 * KeyValueTreeTransformRuleBuilder::Data
466 class KeyValueTreeTransformRuleBuilder::Data
468 public:
469 typedef internal::KeyValueTreeTransformerImpl::Rule Rule;
471 explicit Data(const KeyValueTreePath &prefix)
472 : prefixPath_(prefix), expectedType_(typeid(void)),
473 keyMatchType_(StringCompareType::Exact), keyMatchRule_(false)
477 void createRule(internal::KeyValueTreeTransformerImpl *impl)
479 if (keyMatchRule_)
481 createRuleWithKeyMatchType(impl);
482 return;
484 GMX_RELEASE_ASSERT(transform_ != nullptr,
485 "Transform has not been specified");
486 Rule *rule = impl->getOrCreateRootRule();
487 for (const std::string &key : fromPath_.elements())
489 GMX_RELEASE_ASSERT(rule->targetKey_.empty(),
490 "Cannot specify multiple rules from a single path");
491 rule = rule->getOrCreateChildRule(key);
493 GMX_RELEASE_ASSERT(rule->targetKey_.empty(),
494 "Cannot specify multiple rules from a single path");
495 rule->targetKey_ = toPath_.pop_last();
496 rule->targetPath_ = std::move(toPath_);
497 rule->expectedType_ = expectedType_;
498 rule->transform_ = transform_;
501 void createRuleWithKeyMatchType(internal::KeyValueTreeTransformerImpl *impl)
503 if (fromPath_.empty())
505 impl->createRootRule(keyMatchType_);
507 else
509 std::string lastKey = fromPath_.pop_last();
510 Rule *rule = impl->getOrCreateRootRule();
511 for (const std::string &key : fromPath_.elements())
513 rule = rule->getOrCreateChildRule(key);
515 rule->createChildRule(lastKey, keyMatchType_);
519 const KeyValueTreePath prefixPath_;
520 KeyValueTreePath fromPath_;
521 KeyValueTreePath toPath_;
522 std::type_index expectedType_;
523 Rule::TransformFunction transform_;
524 StringCompareType keyMatchType_;
525 bool keyMatchRule_;
528 /********************************************************************
529 * KeyValueTreeTransformRuleBuilder
532 KeyValueTreeTransformRuleBuilder::KeyValueTreeTransformRuleBuilder(
533 internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
534 : impl_(impl), data_(new Data(prefix))
538 // TODO If the function called here would ever throw
539 // (e.g. std::bad_alloc) then std::terminate will be called.
540 // Alternatively, createRule could catch all exceptions and terminate
541 // but that's the same for an end user. So suppressing the clang-tidy
542 // warning is about as bad as any alternative.
543 KeyValueTreeTransformRuleBuilder::~KeyValueTreeTransformRuleBuilder() // NOLINT(bugprone-exception-escape)
545 if (!std::uncaught_exception())
547 data_->createRule(impl_);
551 void KeyValueTreeTransformRuleBuilder::setFromPath(const KeyValueTreePath &path)
553 data_->fromPath_ = path;
556 void KeyValueTreeTransformRuleBuilder::setExpectedType(const std::type_index &type)
558 data_->expectedType_ = type;
561 void KeyValueTreeTransformRuleBuilder::setToPath(const KeyValueTreePath &path)
563 data_->toPath_ = data_->prefixPath_ + path;
566 void KeyValueTreeTransformRuleBuilder::setKeyMatchType(StringCompareType keyMatchType)
568 data_->keyMatchType_ = keyMatchType;
569 data_->keyMatchRule_ = true;
572 void KeyValueTreeTransformRuleBuilder::addTransformToAny(
573 const std::function<Any(const Any &)> &transform)
575 data_->transform_ =
576 [transform] (KeyValueTreeValueBuilder *builder, const KeyValueTreeValue &value)
578 builder->setAnyValue(transform(value.asAny()));
582 void KeyValueTreeTransformRuleBuilder::addTransformToObject(
583 const std::function<void(KeyValueTreeObjectBuilder *, const Any &)> &transform)
585 data_->transform_ =
586 [transform] (KeyValueTreeValueBuilder *builder, const KeyValueTreeValue &value)
588 KeyValueTreeObjectBuilder obj = builder->createObject();
589 transform(&obj, value.asAny());
593 } // namespace gmx