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/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"
54 /********************************************************************
55 * IKeyValueTreeTransformRules
58 IKeyValueTreeTransformRules::~IKeyValueTreeTransformRules()
62 /********************************************************************
63 * KeyValueTreeTransformRulesScoped::Impl
66 class KeyValueTreeTransformRulesScoped::Impl
: public IKeyValueTreeTransformRules
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
);
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()
116 /********************************************************************
117 * IKeyValueTreeBackMapping
120 IKeyValueTreeBackMapping::~IKeyValueTreeBackMapping()
127 class KeyValueTreeBackMapping
: public IKeyValueTreeBackMapping
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
);
166 KeyValueTreePath sourcePath_
;
167 std::map
<std::string
, Entry
> childEntries_
;
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())
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_
; }
200 /********************************************************************
201 * KeyValueTreeTransformerImpl
204 class KeyValueTreeTransformerImpl
210 typedef std::function
<void(KeyValueTreeValueBuilder
*, const KeyValueTreeValue
&)>
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())
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
;
256 result
->push_back(path
);
260 rule
.collectMappedPaths(path
, result
);
265 KeyValueTreePath targetPath_
;
266 std::string targetKey_
;
267 std::type_index expectedType_
;
268 TransformFunction transform_
;
269 ChildRuleMap childRules_
;
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_
));
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_
))
361 applyTransformedValue(rule
, valueBuilder
.build());
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());
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
);
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()));
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
);
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
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
)
481 createRuleWithKeyMatchType(impl
);
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_
);
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_
;
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
)
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
)
586 [transform
] (KeyValueTreeValueBuilder
*builder
, const KeyValueTreeValue
&value
)
588 KeyValueTreeObjectBuilder obj
= builder
->createObject();
589 transform(&obj
, value
.asAny());