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.
37 #include "keyvaluetreetransform.h"
45 #include "gromacs/compat/make_unique.h"
46 #include "gromacs/utility/exceptions.h"
47 #include "gromacs/utility/ikeyvaluetreeerror.h"
48 #include "gromacs/utility/keyvaluetreebuilder.h"
49 #include "gromacs/utility/stringcompare.h"
50 #include "gromacs/utility/stringutil.h"
55 /********************************************************************
56 * IKeyValueTreeTransformRules
59 IKeyValueTreeTransformRules::~IKeyValueTreeTransformRules()
63 /********************************************************************
64 * KeyValueTreeTransformRulesScoped::Impl
67 class KeyValueTreeTransformRulesScoped::Impl : public IKeyValueTreeTransformRules
70 Impl(internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
71 : impl_(impl), prefix_(prefix)
75 KeyValueTreeTransformRuleBuilder addRule() override
77 return KeyValueTreeTransformRuleBuilder(impl_, prefix_);
80 KeyValueTreeTransformRulesScoped
81 scopedTransform(const KeyValueTreePath &scope) override
83 return KeyValueTreeTransformRulesScoped(impl_, prefix_ + scope);
87 internal::KeyValueTreeTransformerImpl *impl_;
88 KeyValueTreePath prefix_;
91 /********************************************************************
92 * KeyValueTreeTransformRulesScoped
95 KeyValueTreeTransformRulesScoped::KeyValueTreeTransformRulesScoped(
96 internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
97 : impl_(new Impl(impl, prefix))
101 KeyValueTreeTransformRulesScoped::KeyValueTreeTransformRulesScoped(
102 KeyValueTreeTransformRulesScoped &&) noexcept = default;
104 KeyValueTreeTransformRulesScoped &
105 KeyValueTreeTransformRulesScoped::operator=(
106 KeyValueTreeTransformRulesScoped &&) noexcept = default;
108 KeyValueTreeTransformRulesScoped::~KeyValueTreeTransformRulesScoped()
112 IKeyValueTreeTransformRules *KeyValueTreeTransformRulesScoped::rules()
117 /********************************************************************
118 * IKeyValueTreeBackMapping
121 IKeyValueTreeBackMapping::~IKeyValueTreeBackMapping()
128 class KeyValueTreeBackMapping : public IKeyValueTreeBackMapping
135 explicit Entry(const KeyValueTreePath &path) : sourcePath_(path) {}
137 Entry *getOrCreateChildEntry(const std::string &key)
139 auto iter = childEntries_.find(key);
140 if (iter == childEntries_.end())
142 iter = childEntries_.insert(std::make_pair(key, Entry())).first;
144 return &iter->second;
146 void setMapping(const KeyValueTreePath &path,
147 const KeyValueTreeValue &value)
149 GMX_RELEASE_ASSERT(sourcePath_.empty(),
150 "Multiple entries map to same path");
151 if (value.isObject())
153 const KeyValueTreeObject &object = value.asObject();
154 for (const auto &prop : object.properties())
156 GMX_RELEASE_ASSERT(!prop.value().isObject(),
157 "Nested objects not implemented");
158 childEntries_[prop.key()] = Entry(path);
167 KeyValueTreePath sourcePath_;
168 std::map<std::string, Entry> childEntries_;
172 originalPath(const KeyValueTreePath &path) const override
174 const Entry *entry = &rootEntry_;
175 for (const auto &element : path.elements())
177 auto iter = entry->childEntries_.find(element);
178 if (iter == entry->childEntries_.end())
182 entry = &iter->second;
184 GMX_RELEASE_ASSERT(entry->childEntries_.empty()
185 && !entry->sourcePath_.empty(),
186 "Requested path not uniquely mapped");
187 return entry->sourcePath_;
190 Entry *rootEntry() { return &rootEntry_; }
201 /********************************************************************
202 * KeyValueTreeTransformerImpl
205 class KeyValueTreeTransformerImpl
211 typedef std::function<void(KeyValueTreeValueBuilder *, const KeyValueTreeValue &)>
213 typedef std::map<std::string, Rule, StringCompare> ChildRuleMap;
215 explicit Rule(StringCompareType keyMatchType)
216 : expectedType_(typeid(void)), childRules_(keyMatchType)
220 const Rule *findMatchingChildRule(const std::string &key) const
222 auto iter = childRules_.find(key);
223 if (iter == childRules_.end())
227 return &iter->second;
229 Rule *getOrCreateChildRule(const std::string &key)
231 auto iter = childRules_.find(key);
232 if (iter == childRules_.end())
234 return createChildRule(key, StringCompareType::Exact);
236 return &iter->second;
238 Rule *createChildRule(const std::string &key,
239 StringCompareType keyMatchType)
241 auto result = childRules_.insert(std::make_pair(key, Rule(keyMatchType)));
242 GMX_RELEASE_ASSERT(result.second,
243 "Cannot specify key match type after child rules");
244 return &result.first->second;
247 void collectMappedPaths(const KeyValueTreePath &prefix,
248 std::vector<KeyValueTreePath> *result) const
250 for (const auto &value : childRules_)
252 KeyValueTreePath path = prefix;
253 path.append(value.first);
254 const Rule &rule = value.second;
257 result->push_back(path);
261 rule.collectMappedPaths(path, result);
266 KeyValueTreePath targetPath_;
267 std::string targetKey_;
268 std::type_index expectedType_;
269 TransformFunction transform_;
270 ChildRuleMap childRules_;
276 explicit Transformer(IKeyValueTreeErrorHandler *errorHandler)
277 : errorHandler_(errorHandler),
278 backMapping_(new KeyValueTreeBackMapping)
280 if (errorHandler_ == nullptr)
282 errorHandler_ = defaultKeyValueTreeErrorHandler();
286 void transform(const Rule *rootRule, const KeyValueTreeObject &tree)
288 if (rootRule != nullptr)
290 doChildTransforms(rootRule, tree);
294 KeyValueTreeTransformResult result()
296 return KeyValueTreeTransformResult(builder_.build(),
297 std::move(backMapping_));
301 void doTransform(const Rule *rule, const KeyValueTreeValue &value);
302 void doChildTransforms(const Rule *rule, const KeyValueTreeObject &object);
303 void applyTransformedValue(const Rule *rule, KeyValueTreeValue &&value);
305 IKeyValueTreeErrorHandler *errorHandler_;
306 KeyValueTreeBuilder builder_;
307 std::unique_ptr<KeyValueTreeBackMapping> backMapping_;
308 KeyValueTreePath context_;
311 KeyValueTreeTransformerImpl()
312 : rootScope_(this, KeyValueTreePath())
316 Rule *getOrCreateRootRule()
318 if (rootRule_ == nullptr)
320 createRootRule(StringCompareType::Exact);
322 return rootRule_.get();
324 void createRootRule(StringCompareType keyMatchType)
326 GMX_RELEASE_ASSERT(rootRule_ == nullptr,
327 "Cannot specify key match type after child rules");
328 rootRule_ = compat::make_unique<Rule>(keyMatchType);
331 std::unique_ptr<Rule> rootRule_;
332 KeyValueTreeTransformRulesScoped rootScope_;
335 /********************************************************************
336 * KeyValueTreeTransformerImpl::Transformer
339 void KeyValueTreeTransformerImpl::Transformer::doTransform(
340 const Rule *rule, const KeyValueTreeValue &value)
342 if (rule->transform_ != nullptr)
344 KeyValueTreeValueBuilder valueBuilder;
347 if (value.type() != rule->expectedType_)
349 // TODO: Better error message.
350 GMX_THROW(InvalidInputError("Unexpected type of value"));
352 rule->transform_(&valueBuilder, value);
354 catch (UserInputError &ex)
356 if (!errorHandler_->onError(&ex, context_))
362 applyTransformedValue(rule, valueBuilder.build());
365 if (!rule->childRules_.empty())
367 doChildTransforms(rule, value.asObject());
371 void KeyValueTreeTransformerImpl::Transformer::doChildTransforms(
372 const Rule *rule, const KeyValueTreeObject &object)
374 for (const auto &prop : object.properties())
376 const Rule *childRule = rule->findMatchingChildRule(prop.key());
377 if (childRule != nullptr)
379 context_.append(prop.key());
380 doTransform(childRule, prop.value());
386 void KeyValueTreeTransformerImpl::Transformer::applyTransformedValue(
387 const Rule *rule, KeyValueTreeValue &&value)
389 KeyValueTreeObjectBuilder objBuilder = builder_.rootObject();
390 KeyValueTreeBackMapping::Entry *mapEntry = backMapping_->rootEntry();
391 for (const std::string &key : rule->targetPath_.elements())
393 if (objBuilder.keyExists(key))
395 GMX_RELEASE_ASSERT(objBuilder[key].isObject(),
396 "Inconsistent transform (different items map to same path)");
397 objBuilder = objBuilder.getObjectBuilder(key);
401 objBuilder = objBuilder.addObject(key);
403 mapEntry = mapEntry->getOrCreateChildEntry(key);
405 mapEntry = mapEntry->getOrCreateChildEntry(rule->targetKey_);
406 mapEntry->setMapping(context_, value);
407 if (objBuilder.keyExists(rule->targetKey_))
409 GMX_RELEASE_ASSERT(value.isObject(),
410 "Inconsistent transform (different items map to same path)");
411 GMX_RELEASE_ASSERT(objBuilder[rule->targetKey_].isObject(),
412 "Inconsistent transform (different items map to same path)");
413 objBuilder = objBuilder.getObjectBuilder(rule->targetKey_);
414 GMX_RELEASE_ASSERT(objBuilder.objectHasDistinctProperties(value.asObject()),
415 "Inconsistent transform (different items map to same path)");
416 objBuilder.mergeObject(std::move(value.asObject()));
420 objBuilder.addRawValue(rule->targetKey_, std::move(value));
424 } // namespace internal
426 /********************************************************************
427 * KeyValueTreeTransformer
430 KeyValueTreeTransformer::KeyValueTreeTransformer()
431 : impl_(new internal::KeyValueTreeTransformerImpl)
435 KeyValueTreeTransformer::~KeyValueTreeTransformer()
439 IKeyValueTreeTransformRules *KeyValueTreeTransformer::rules()
441 return impl_->rootScope_.rules();
444 std::vector<KeyValueTreePath> KeyValueTreeTransformer::mappedPaths() const
446 std::vector<KeyValueTreePath> result;
447 if (impl_->rootRule_)
449 impl_->rootRule_->collectMappedPaths(KeyValueTreePath(), &result);
454 KeyValueTreeTransformResult
455 KeyValueTreeTransformer::transform(const KeyValueTreeObject &tree,
456 IKeyValueTreeErrorHandler *errorHandler) const
458 internal::KeyValueTreeTransformerImpl::Transformer transformer(errorHandler);
459 transformer.transform(impl_->rootRule_.get(), tree);
460 return transformer.result();
463 /********************************************************************
464 * KeyValueTreeTransformRuleBuilder::Data
467 class KeyValueTreeTransformRuleBuilder::Data
470 typedef internal::KeyValueTreeTransformerImpl::Rule Rule;
472 explicit Data(const KeyValueTreePath &prefix)
473 : prefixPath_(prefix), expectedType_(typeid(void)),
474 keyMatchType_(StringCompareType::Exact), keyMatchRule_(false)
478 void createRule(internal::KeyValueTreeTransformerImpl *impl)
482 createRuleWithKeyMatchType(impl);
485 GMX_RELEASE_ASSERT(transform_ != nullptr,
486 "Transform has not been specified");
487 Rule *rule = impl->getOrCreateRootRule();
488 for (const std::string &key : fromPath_.elements())
490 GMX_RELEASE_ASSERT(rule->targetKey_.empty(),
491 "Cannot specify multiple rules from a single path");
492 rule = rule->getOrCreateChildRule(key);
494 GMX_RELEASE_ASSERT(rule->targetKey_.empty(),
495 "Cannot specify multiple rules from a single path");
496 rule->targetKey_ = toPath_.pop_last();
497 rule->targetPath_ = std::move(toPath_);
498 rule->expectedType_ = expectedType_;
499 rule->transform_ = transform_;
502 void createRuleWithKeyMatchType(internal::KeyValueTreeTransformerImpl *impl)
504 if (fromPath_.empty())
506 impl->createRootRule(keyMatchType_);
510 std::string lastKey = fromPath_.pop_last();
511 Rule *rule = impl->getOrCreateRootRule();
512 for (const std::string &key : fromPath_.elements())
514 rule = rule->getOrCreateChildRule(key);
516 rule->createChildRule(lastKey, keyMatchType_);
520 const KeyValueTreePath prefixPath_;
521 KeyValueTreePath fromPath_;
522 KeyValueTreePath toPath_;
523 std::type_index expectedType_;
524 Rule::TransformFunction transform_;
525 StringCompareType keyMatchType_;
529 /********************************************************************
530 * KeyValueTreeTransformRuleBuilder
533 KeyValueTreeTransformRuleBuilder::KeyValueTreeTransformRuleBuilder(
534 internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
535 : impl_(impl), data_(new Data(prefix))
539 KeyValueTreeTransformRuleBuilder::~KeyValueTreeTransformRuleBuilder()
541 if (!std::uncaught_exception())
543 data_->createRule(impl_);
547 void KeyValueTreeTransformRuleBuilder::setFromPath(const KeyValueTreePath &path)
549 data_->fromPath_ = path;
552 void KeyValueTreeTransformRuleBuilder::setExpectedType(const std::type_index &type)
554 data_->expectedType_ = type;
557 void KeyValueTreeTransformRuleBuilder::setToPath(const KeyValueTreePath &path)
559 data_->toPath_ = data_->prefixPath_ + path;
562 void KeyValueTreeTransformRuleBuilder::setKeyMatchType(StringCompareType keyMatchType)
564 data_->keyMatchType_ = keyMatchType;
565 data_->keyMatchRule_ = true;
568 void KeyValueTreeTransformRuleBuilder::addTransformToAny(
569 const std::function<Any(const Any &)> &transform)
572 [transform] (KeyValueTreeValueBuilder *builder, const KeyValueTreeValue &value)
574 builder->setAnyValue(transform(value.asAny()));
578 void KeyValueTreeTransformRuleBuilder::addTransformToObject(
579 const std::function<void(KeyValueTreeObjectBuilder *, const Any &)> &transform)
582 [transform] (KeyValueTreeValueBuilder *builder, const KeyValueTreeValue &value)
584 KeyValueTreeObjectBuilder obj = builder->createObject();
585 transform(&obj, value.asAny());