SYCL: Avoid using no_init read accessor in rocFFT
[alexxy/gromacs.git] / src / gromacs / utility / keyvaluetreetransform.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2016,2017,2018,2019,2020,2021, 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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 #include "gmxpre.h"
36
37 #include "keyvaluetreetransform.h"
38
39 #include <functional>
40 #include <map>
41 #include <memory>
42 #include <typeindex>
43 #include <vector>
44
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"
50
51 namespace gmx
52 {
53
54 /********************************************************************
55  * IKeyValueTreeTransformRules
56  */
57
58 IKeyValueTreeTransformRules::~IKeyValueTreeTransformRules() {}
59
60 /********************************************************************
61  * KeyValueTreeTransformRulesScoped::Impl
62  */
63
64 class KeyValueTreeTransformRulesScoped::Impl : public IKeyValueTreeTransformRules
65 {
66 public:
67     Impl(internal::KeyValueTreeTransformerImpl* impl, const KeyValueTreePath& prefix) :
68         impl_(impl), prefix_(prefix)
69     {
70     }
71
72     KeyValueTreeTransformRuleBuilder addRule() override
73     {
74         return KeyValueTreeTransformRuleBuilder(impl_, prefix_);
75     }
76
77     KeyValueTreeTransformRulesScoped scopedTransform(const KeyValueTreePath& scope) override
78     {
79         return KeyValueTreeTransformRulesScoped(impl_, prefix_ + scope);
80     }
81
82 private:
83     internal::KeyValueTreeTransformerImpl* impl_;
84     KeyValueTreePath                       prefix_;
85 };
86
87 /********************************************************************
88  * KeyValueTreeTransformRulesScoped
89  */
90
91 KeyValueTreeTransformRulesScoped::KeyValueTreeTransformRulesScoped(internal::KeyValueTreeTransformerImpl* impl,
92                                                                    const KeyValueTreePath& prefix) :
93     impl_(new Impl(impl, prefix))
94 {
95 }
96
97 KeyValueTreeTransformRulesScoped::KeyValueTreeTransformRulesScoped(KeyValueTreeTransformRulesScoped&&) noexcept = default;
98
99 KeyValueTreeTransformRulesScoped&
100 KeyValueTreeTransformRulesScoped::operator=(KeyValueTreeTransformRulesScoped&&) noexcept = default;
101
102 KeyValueTreeTransformRulesScoped::~KeyValueTreeTransformRulesScoped() {}
103
104 IKeyValueTreeTransformRules* KeyValueTreeTransformRulesScoped::rules()
105 {
106     return impl_.get();
107 }
108
109 /********************************************************************
110  * IKeyValueTreeBackMapping
111  */
112
113 IKeyValueTreeBackMapping::~IKeyValueTreeBackMapping() {}
114
115 namespace
116 {
117
118 class KeyValueTreeBackMapping : public IKeyValueTreeBackMapping
119 {
120 public:
121     class Entry
122     {
123     public:
124         Entry() = default;
125         explicit Entry(const KeyValueTreePath& path) : sourcePath_(path) {}
126
127         Entry* getOrCreateChildEntry(const std::string& key)
128         {
129             auto iter = childEntries_.find(key);
130             if (iter == childEntries_.end())
131             {
132                 iter = childEntries_.insert(std::make_pair(key, Entry())).first;
133             }
134             return &iter->second;
135         }
136         void setMapping(const KeyValueTreePath& path, const KeyValueTreeValue& value)
137         {
138             GMX_RELEASE_ASSERT(sourcePath_.empty(), "Multiple entries map to same path");
139             if (value.isObject())
140             {
141                 const KeyValueTreeObject& object = value.asObject();
142                 for (const auto& prop : object.properties())
143                 {
144                     GMX_RELEASE_ASSERT(!prop.value().isObject(), "Nested objects not implemented");
145                     childEntries_[prop.key()] = Entry(path);
146                 }
147             }
148             else
149             {
150                 sourcePath_ = path;
151             }
152         }
153
154         KeyValueTreePath             sourcePath_;
155         std::map<std::string, Entry> childEntries_;
156     };
157
158     KeyValueTreePath originalPath(const KeyValueTreePath& path) const override
159     {
160         const Entry* entry = &rootEntry_;
161         for (const auto& element : path.elements())
162         {
163             auto iter = entry->childEntries_.find(element);
164             if (iter == entry->childEntries_.end())
165             {
166                 break;
167             }
168             entry = &iter->second;
169         }
170         GMX_RELEASE_ASSERT(entry->childEntries_.empty() && !entry->sourcePath_.empty(),
171                            "Requested path not uniquely mapped");
172         return entry->sourcePath_;
173     }
174
175     Entry* rootEntry() { return &rootEntry_; }
176
177 private:
178     Entry rootEntry_;
179 };
180
181 } // namespace
182
183 namespace internal
184 {
185
186 /********************************************************************
187  * KeyValueTreeTransformerImpl
188  */
189
190 class KeyValueTreeTransformerImpl
191 {
192 public:
193     class Rule
194     {
195     public:
196         typedef std::function<void(KeyValueTreeValueBuilder*, const KeyValueTreeValue&)> TransformFunction;
197         typedef std::map<std::string, Rule, StringCompare> ChildRuleMap;
198
199         explicit Rule(StringCompareType keyMatchType) :
200             expectedType_(typeid(void)), childRules_(keyMatchType)
201         {
202         }
203
204         const Rule* findMatchingChildRule(const std::string& key) const
205         {
206             auto iter = childRules_.find(key);
207             if (iter == childRules_.end())
208             {
209                 return nullptr;
210             }
211             return &iter->second;
212         }
213         Rule* getOrCreateChildRule(const std::string& key)
214         {
215             auto iter = childRules_.find(key);
216             if (iter == childRules_.end())
217             {
218                 return createChildRule(key, StringCompareType::Exact);
219             }
220             return &iter->second;
221         }
222         Rule* createChildRule(const std::string& key, StringCompareType keyMatchType)
223         {
224             auto result = childRules_.insert(std::make_pair(key, Rule(keyMatchType)));
225             GMX_RELEASE_ASSERT(result.second, "Cannot specify key match type after child rules");
226             return &result.first->second;
227         }
228
229         void collectMappedPaths(const KeyValueTreePath& prefix, std::vector<KeyValueTreePath>* result) const
230         {
231             for (const auto& value : childRules_)
232             {
233                 KeyValueTreePath path = prefix;
234                 path.append(value.first);
235                 const Rule& rule = value.second;
236                 if (rule.transform_)
237                 {
238                     result->push_back(path);
239                 }
240                 else
241                 {
242                     rule.collectMappedPaths(path, result);
243                 }
244             }
245         }
246
247         KeyValueTreePath  targetPath_;
248         std::string       targetKey_;
249         std::type_index   expectedType_;
250         TransformFunction transform_;
251         ChildRuleMap      childRules_;
252     };
253
254     class Transformer
255     {
256     public:
257         explicit Transformer(IKeyValueTreeErrorHandler* errorHandler) :
258             errorHandler_(errorHandler), backMapping_(new KeyValueTreeBackMapping)
259         {
260             if (errorHandler_ == nullptr)
261             {
262                 errorHandler_ = defaultKeyValueTreeErrorHandler();
263             }
264         }
265
266         void transform(const Rule* rootRule, const KeyValueTreeObject& tree)
267         {
268             if (rootRule != nullptr)
269             {
270                 doChildTransforms(rootRule, tree);
271             }
272         }
273
274         KeyValueTreeTransformResult result()
275         {
276             return KeyValueTreeTransformResult(builder_.build(), std::move(backMapping_));
277         }
278
279     private:
280         void doTransform(const Rule* rule, const KeyValueTreeValue& value);
281         void doChildTransforms(const Rule* rule, const KeyValueTreeObject& object);
282         void applyTransformedValue(const Rule* rule, KeyValueTreeValue&& value);
283
284         IKeyValueTreeErrorHandler*               errorHandler_;
285         KeyValueTreeBuilder                      builder_;
286         std::unique_ptr<KeyValueTreeBackMapping> backMapping_;
287         KeyValueTreePath                         context_;
288     };
289
290     KeyValueTreeTransformerImpl() : rootScope_(this, KeyValueTreePath()) {}
291
292     Rule* getOrCreateRootRule()
293     {
294         if (rootRule_ == nullptr)
295         {
296             createRootRule(StringCompareType::Exact);
297         }
298         return rootRule_.get();
299     }
300     void createRootRule(StringCompareType keyMatchType)
301     {
302         GMX_RELEASE_ASSERT(rootRule_ == nullptr, "Cannot specify key match type after child rules");
303         rootRule_ = std::make_unique<Rule>(keyMatchType);
304     }
305
306     std::unique_ptr<Rule>            rootRule_;
307     KeyValueTreeTransformRulesScoped rootScope_;
308 };
309
310 /********************************************************************
311  * KeyValueTreeTransformerImpl::Transformer
312  */
313
314 void KeyValueTreeTransformerImpl::Transformer::doTransform(const Rule* rule, const KeyValueTreeValue& value)
315 {
316     if (rule->transform_ != nullptr)
317     {
318         KeyValueTreeValueBuilder valueBuilder;
319         try
320         {
321             if (value.type() != rule->expectedType_)
322             {
323                 // TODO: Better error message.
324                 GMX_THROW(InvalidInputError("Unexpected type of value"));
325             }
326             rule->transform_(&valueBuilder, value);
327         }
328         catch (UserInputError& ex)
329         {
330             if (!errorHandler_->onError(&ex, context_))
331             {
332                 throw;
333             }
334             return;
335         }
336         applyTransformedValue(rule, valueBuilder.build());
337         return;
338     }
339     if (!rule->childRules_.empty())
340     {
341         doChildTransforms(rule, value.asObject());
342     }
343 }
344
345 void KeyValueTreeTransformerImpl::Transformer::doChildTransforms(const Rule*               rule,
346                                                                  const KeyValueTreeObject& object)
347 {
348     for (const auto& prop : object.properties())
349     {
350         const Rule* childRule = rule->findMatchingChildRule(prop.key());
351         if (childRule != nullptr)
352         {
353             context_.append(prop.key());
354             doTransform(childRule, prop.value());
355             context_.pop_back();
356         }
357     }
358 }
359
360 void KeyValueTreeTransformerImpl::Transformer::applyTransformedValue(const Rule*         rule,
361                                                                      KeyValueTreeValue&& value)
362 {
363     KeyValueTreeObjectBuilder       objBuilder = builder_.rootObject();
364     KeyValueTreeBackMapping::Entry* mapEntry   = backMapping_->rootEntry();
365     for (const std::string& key : rule->targetPath_.elements())
366     {
367         if (objBuilder.keyExists(key))
368         {
369             GMX_RELEASE_ASSERT(objBuilder[key].isObject(),
370                                "Inconsistent transform (different items map to same path)");
371             objBuilder = objBuilder.getObjectBuilder(key);
372         }
373         else
374         {
375             objBuilder = objBuilder.addObject(key);
376         }
377         mapEntry = mapEntry->getOrCreateChildEntry(key);
378     }
379     mapEntry = mapEntry->getOrCreateChildEntry(rule->targetKey_);
380     mapEntry->setMapping(context_, value);
381     if (objBuilder.keyExists(rule->targetKey_))
382     {
383         GMX_RELEASE_ASSERT(value.isObject(),
384                            "Inconsistent transform (different items map to same path)");
385         GMX_RELEASE_ASSERT(objBuilder[rule->targetKey_].isObject(),
386                            "Inconsistent transform (different items map to same path)");
387         objBuilder = objBuilder.getObjectBuilder(rule->targetKey_);
388         GMX_RELEASE_ASSERT(objBuilder.objectHasDistinctProperties(value.asObject()),
389                            "Inconsistent transform (different items map to same path)");
390         objBuilder.mergeObject(std::move(value.asObject()));
391     }
392     else
393     {
394         objBuilder.addRawValue(rule->targetKey_, std::move(value));
395     }
396 }
397
398 } // namespace internal
399
400 /********************************************************************
401  * KeyValueTreeTransformer
402  */
403
404 KeyValueTreeTransformer::KeyValueTreeTransformer() :
405     impl_(new internal::KeyValueTreeTransformerImpl)
406 {
407 }
408
409 KeyValueTreeTransformer::~KeyValueTreeTransformer() {}
410
411 IKeyValueTreeTransformRules* KeyValueTreeTransformer::rules()
412 {
413     return impl_->rootScope_.rules();
414 }
415
416 std::vector<KeyValueTreePath> KeyValueTreeTransformer::mappedPaths() const
417 {
418     std::vector<KeyValueTreePath> result;
419     if (impl_->rootRule_)
420     {
421         impl_->rootRule_->collectMappedPaths(KeyValueTreePath(), &result);
422     }
423     return result;
424 }
425
426 KeyValueTreeTransformResult KeyValueTreeTransformer::transform(const KeyValueTreeObject& tree,
427                                                                IKeyValueTreeErrorHandler* errorHandler) const
428 {
429     internal::KeyValueTreeTransformerImpl::Transformer transformer(errorHandler);
430     transformer.transform(impl_->rootRule_.get(), tree);
431     return transformer.result();
432 }
433
434 /********************************************************************
435  * KeyValueTreeTransformRuleBuilder::Data
436  */
437
438 class KeyValueTreeTransformRuleBuilder::Data
439 {
440 public:
441     typedef internal::KeyValueTreeTransformerImpl::Rule Rule;
442
443     explicit Data(const KeyValueTreePath& prefix) :
444         prefixPath_(prefix),
445         expectedType_(typeid(void)),
446         keyMatchType_(StringCompareType::Exact),
447         keyMatchRule_(false)
448     {
449     }
450
451     void createRule(internal::KeyValueTreeTransformerImpl* impl)
452     {
453         if (keyMatchRule_)
454         {
455             createRuleWithKeyMatchType(impl);
456             return;
457         }
458         GMX_RELEASE_ASSERT(transform_ != nullptr, "Transform has not been specified");
459         Rule* rule = impl->getOrCreateRootRule();
460         for (const std::string& key : fromPath_.elements())
461         {
462             GMX_RELEASE_ASSERT(rule->targetKey_.empty(),
463                                "Cannot specify multiple rules from a single path");
464             rule = rule->getOrCreateChildRule(key);
465         }
466         GMX_RELEASE_ASSERT(rule->targetKey_.empty(),
467                            "Cannot specify multiple rules from a single path");
468         rule->targetKey_    = toPath_.pop_last();
469         rule->targetPath_   = std::move(toPath_);
470         rule->expectedType_ = expectedType_;
471         rule->transform_    = transform_;
472     }
473
474     void createRuleWithKeyMatchType(internal::KeyValueTreeTransformerImpl* impl)
475     {
476         if (fromPath_.empty())
477         {
478             impl->createRootRule(keyMatchType_);
479         }
480         else
481         {
482             std::string lastKey = fromPath_.pop_last();
483             Rule*       rule    = impl->getOrCreateRootRule();
484             for (const std::string& key : fromPath_.elements())
485             {
486                 rule = rule->getOrCreateChildRule(key);
487             }
488             rule->createChildRule(lastKey, keyMatchType_);
489         }
490     }
491
492     const KeyValueTreePath  prefixPath_;
493     KeyValueTreePath        fromPath_;
494     KeyValueTreePath        toPath_;
495     std::type_index         expectedType_;
496     Rule::TransformFunction transform_;
497     StringCompareType       keyMatchType_;
498     bool                    keyMatchRule_;
499 };
500
501 /********************************************************************
502  * KeyValueTreeTransformRuleBuilder
503  */
504
505 KeyValueTreeTransformRuleBuilder::KeyValueTreeTransformRuleBuilder(internal::KeyValueTreeTransformerImpl* impl,
506                                                                    const KeyValueTreePath& prefix) :
507     impl_(impl), data_(new Data(prefix))
508 {
509 }
510
511 // TODO If the function called here would ever throw
512 // (e.g. std::bad_alloc) then std::terminate will be called.
513 // Alternatively, createRule could catch all exceptions and terminate
514 // but that's the same for an end user. So suppressing the clang-tidy
515 // warning is about as bad as any alternative.
516 KeyValueTreeTransformRuleBuilder::~KeyValueTreeTransformRuleBuilder() // NOLINT(bugprone-exception-escape)
517 {
518     if (!std::uncaught_exceptions())
519     {
520         data_->createRule(impl_);
521     }
522 }
523
524 void KeyValueTreeTransformRuleBuilder::setFromPath(const KeyValueTreePath& path)
525 {
526     data_->fromPath_ = path;
527 }
528
529 void KeyValueTreeTransformRuleBuilder::setExpectedType(const std::type_index& type)
530 {
531     data_->expectedType_ = type;
532 }
533
534 void KeyValueTreeTransformRuleBuilder::setToPath(const KeyValueTreePath& path)
535 {
536     data_->toPath_ = data_->prefixPath_ + path;
537 }
538
539 void KeyValueTreeTransformRuleBuilder::setKeyMatchType(StringCompareType keyMatchType)
540 {
541     data_->keyMatchType_ = keyMatchType;
542     data_->keyMatchRule_ = true;
543 }
544
545 void KeyValueTreeTransformRuleBuilder::addTransformToAny(const std::function<Any(const Any&)>& transform)
546 {
547     data_->transform_ = [transform](KeyValueTreeValueBuilder* builder, const KeyValueTreeValue& value) {
548         builder->setAnyValue(transform(value.asAny()));
549     };
550 }
551
552 void KeyValueTreeTransformRuleBuilder::addTransformToObject(
553         const std::function<void(KeyValueTreeObjectBuilder*, const Any&)>& transform)
554 {
555     data_->transform_ = [transform](KeyValueTreeValueBuilder* builder, const KeyValueTreeValue& value) {
556         KeyValueTreeObjectBuilder obj = builder->createObject();
557         transform(&obj, value.asAny());
558     };
559 }
560
561 } // namespace gmx