Replace compat::make_unique with std::make_unique
[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, 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
62 /********************************************************************
63  * KeyValueTreeTransformRulesScoped::Impl
64  */
65
66 class KeyValueTreeTransformRulesScoped::Impl : public IKeyValueTreeTransformRules
67 {
68     public:
69         Impl(internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
70             : impl_(impl), prefix_(prefix)
71         {
72         }
73
74         KeyValueTreeTransformRuleBuilder addRule() override
75         {
76             return KeyValueTreeTransformRuleBuilder(impl_, prefix_);
77         }
78
79         KeyValueTreeTransformRulesScoped
80         scopedTransform(const KeyValueTreePath &scope) override
81         {
82             return KeyValueTreeTransformRulesScoped(impl_, prefix_ + scope);
83         }
84
85     private:
86         internal::KeyValueTreeTransformerImpl *impl_;
87         KeyValueTreePath                       prefix_;
88 };
89
90 /********************************************************************
91  * KeyValueTreeTransformRulesScoped
92  */
93
94 KeyValueTreeTransformRulesScoped::KeyValueTreeTransformRulesScoped(
95         internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
96     : impl_(new Impl(impl, prefix))
97 {
98 }
99
100 KeyValueTreeTransformRulesScoped::KeyValueTreeTransformRulesScoped(
101         KeyValueTreeTransformRulesScoped &&) noexcept = default;
102
103 KeyValueTreeTransformRulesScoped &
104 KeyValueTreeTransformRulesScoped::operator=(
105         KeyValueTreeTransformRulesScoped &&) noexcept = default;
106
107 KeyValueTreeTransformRulesScoped::~KeyValueTreeTransformRulesScoped()
108 {
109 }
110
111 IKeyValueTreeTransformRules *KeyValueTreeTransformRulesScoped::rules()
112 {
113     return impl_.get();
114 }
115
116 /********************************************************************
117  * IKeyValueTreeBackMapping
118  */
119
120 IKeyValueTreeBackMapping::~IKeyValueTreeBackMapping()
121 {
122 }
123
124 namespace
125 {
126
127 class KeyValueTreeBackMapping : public IKeyValueTreeBackMapping
128 {
129     public:
130         class Entry
131         {
132             public:
133                 Entry() = default;
134                 explicit Entry(const KeyValueTreePath &path) : sourcePath_(path) {}
135
136                 Entry *getOrCreateChildEntry(const std::string &key)
137                 {
138                     auto iter = childEntries_.find(key);
139                     if (iter == childEntries_.end())
140                     {
141                         iter = childEntries_.insert(std::make_pair(key, Entry())).first;
142                     }
143                     return &iter->second;
144                 }
145                 void setMapping(const KeyValueTreePath  &path,
146                                 const KeyValueTreeValue &value)
147                 {
148                     GMX_RELEASE_ASSERT(sourcePath_.empty(),
149                                        "Multiple entries map to same path");
150                     if (value.isObject())
151                     {
152                         const KeyValueTreeObject &object = value.asObject();
153                         for (const auto &prop : object.properties())
154                         {
155                             GMX_RELEASE_ASSERT(!prop.value().isObject(),
156                                                "Nested objects not implemented");
157                             childEntries_[prop.key()] = Entry(path);
158                         }
159                     }
160                     else
161                     {
162                         sourcePath_ = path;
163                     }
164                 }
165
166                 KeyValueTreePath             sourcePath_;
167                 std::map<std::string, Entry> childEntries_;
168         };
169
170         KeyValueTreePath
171         originalPath(const KeyValueTreePath &path) const override
172         {
173             const Entry *entry = &rootEntry_;
174             for (const auto &element : path.elements())
175             {
176                 auto iter = entry->childEntries_.find(element);
177                 if (iter == entry->childEntries_.end())
178                 {
179                     break;
180                 }
181                 entry = &iter->second;
182             }
183             GMX_RELEASE_ASSERT(entry->childEntries_.empty()
184                                && !entry->sourcePath_.empty(),
185                                "Requested path not uniquely mapped");
186             return entry->sourcePath_;
187         }
188
189         Entry *rootEntry() { return &rootEntry_; }
190
191     private:
192         Entry rootEntry_;
193 };
194
195 }   // namespace
196
197 namespace internal
198 {
199
200 /********************************************************************
201  * KeyValueTreeTransformerImpl
202  */
203
204 class KeyValueTreeTransformerImpl
205 {
206     public:
207         class Rule
208         {
209             public:
210                 typedef std::function<void(KeyValueTreeValueBuilder *, const KeyValueTreeValue &)>
211                     TransformFunction;
212                 typedef std::map<std::string, Rule, StringCompare> ChildRuleMap;
213
214                 explicit Rule(StringCompareType keyMatchType)
215                     : expectedType_(typeid(void)), childRules_(keyMatchType)
216                 {
217                 }
218
219                 const Rule *findMatchingChildRule(const std::string &key) const
220                 {
221                     auto iter = childRules_.find(key);
222                     if (iter == childRules_.end())
223                     {
224                         return nullptr;
225                     }
226                     return &iter->second;
227                 }
228                 Rule *getOrCreateChildRule(const std::string &key)
229                 {
230                     auto iter = childRules_.find(key);
231                     if (iter == childRules_.end())
232                     {
233                         return createChildRule(key, StringCompareType::Exact);
234                     }
235                     return &iter->second;
236                 }
237                 Rule *createChildRule(const std::string &key,
238                                       StringCompareType  keyMatchType)
239                 {
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;
244                 }
245
246                 void collectMappedPaths(const KeyValueTreePath        &prefix,
247                                         std::vector<KeyValueTreePath> *result) const
248                 {
249                     for (const auto &value : childRules_)
250                     {
251                         KeyValueTreePath path = prefix;
252                         path.append(value.first);
253                         const Rule      &rule = value.second;
254                         if (rule.transform_)
255                         {
256                             result->push_back(path);
257                         }
258                         else
259                         {
260                             rule.collectMappedPaths(path, result);
261                         }
262                     }
263                 }
264
265                 KeyValueTreePath            targetPath_;
266                 std::string                 targetKey_;
267                 std::type_index             expectedType_;
268                 TransformFunction           transform_;
269                 ChildRuleMap                childRules_;
270         };
271
272         class Transformer
273         {
274             public:
275                 explicit Transformer(IKeyValueTreeErrorHandler *errorHandler)
276                     : errorHandler_(errorHandler),
277                       backMapping_(new KeyValueTreeBackMapping)
278                 {
279                     if (errorHandler_ == nullptr)
280                     {
281                         errorHandler_ = defaultKeyValueTreeErrorHandler();
282                     }
283                 }
284
285                 void transform(const Rule *rootRule, const KeyValueTreeObject &tree)
286                 {
287                     if (rootRule != nullptr)
288                     {
289                         doChildTransforms(rootRule, tree);
290                     }
291                 }
292
293                 KeyValueTreeTransformResult result()
294                 {
295                     return KeyValueTreeTransformResult(builder_.build(),
296                                                        std::move(backMapping_));
297                 }
298
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);
303
304                 IKeyValueTreeErrorHandler               *errorHandler_;
305                 KeyValueTreeBuilder                      builder_;
306                 std::unique_ptr<KeyValueTreeBackMapping> backMapping_;
307                 KeyValueTreePath                         context_;
308         };
309
310         KeyValueTreeTransformerImpl()
311             : rootScope_(this, KeyValueTreePath())
312         {
313         }
314
315         Rule *getOrCreateRootRule()
316         {
317             if (rootRule_ == nullptr)
318             {
319                 createRootRule(StringCompareType::Exact);
320             }
321             return rootRule_.get();
322         }
323         void createRootRule(StringCompareType keyMatchType)
324         {
325             GMX_RELEASE_ASSERT(rootRule_ == nullptr,
326                                "Cannot specify key match type after child rules");
327             rootRule_ = std::make_unique<Rule>(keyMatchType);
328         }
329
330         std::unique_ptr<Rule>             rootRule_;
331         KeyValueTreeTransformRulesScoped  rootScope_;
332 };
333
334 /********************************************************************
335  * KeyValueTreeTransformerImpl::Transformer
336  */
337
338 void KeyValueTreeTransformerImpl::Transformer::doTransform(
339         const Rule *rule, const KeyValueTreeValue &value)
340 {
341     if (rule->transform_ != nullptr)
342     {
343         KeyValueTreeValueBuilder valueBuilder;
344         try
345         {
346             if (value.type() != rule->expectedType_)
347             {
348                 // TODO: Better error message.
349                 GMX_THROW(InvalidInputError("Unexpected type of value"));
350             }
351             rule->transform_(&valueBuilder, value);
352         }
353         catch (UserInputError &ex)
354         {
355             if (!errorHandler_->onError(&ex, context_))
356             {
357                 throw;
358             }
359             return;
360         }
361         applyTransformedValue(rule, valueBuilder.build());
362         return;
363     }
364     if (!rule->childRules_.empty())
365     {
366         doChildTransforms(rule, value.asObject());
367     }
368 }
369
370 void KeyValueTreeTransformerImpl::Transformer::doChildTransforms(
371         const Rule *rule, const KeyValueTreeObject &object)
372 {
373     for (const auto &prop : object.properties())
374     {
375         const Rule *childRule = rule->findMatchingChildRule(prop.key());
376         if (childRule != nullptr)
377         {
378             context_.append(prop.key());
379             doTransform(childRule, prop.value());
380             context_.pop_back();
381         }
382     }
383 }
384
385 void KeyValueTreeTransformerImpl::Transformer::applyTransformedValue(
386         const Rule *rule, KeyValueTreeValue &&value)
387 {
388     KeyValueTreeObjectBuilder       objBuilder = builder_.rootObject();
389     KeyValueTreeBackMapping::Entry *mapEntry   = backMapping_->rootEntry();
390     for (const std::string &key : rule->targetPath_.elements())
391     {
392         if (objBuilder.keyExists(key))
393         {
394             GMX_RELEASE_ASSERT(objBuilder[key].isObject(),
395                                "Inconsistent transform (different items map to same path)");
396             objBuilder = objBuilder.getObjectBuilder(key);
397         }
398         else
399         {
400             objBuilder = objBuilder.addObject(key);
401         }
402         mapEntry = mapEntry->getOrCreateChildEntry(key);
403     }
404     mapEntry = mapEntry->getOrCreateChildEntry(rule->targetKey_);
405     mapEntry->setMapping(context_, value);
406     if (objBuilder.keyExists(rule->targetKey_))
407     {
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()));
416     }
417     else
418     {
419         objBuilder.addRawValue(rule->targetKey_, std::move(value));
420     }
421 }
422
423 }   // namespace internal
424
425 /********************************************************************
426  * KeyValueTreeTransformer
427  */
428
429 KeyValueTreeTransformer::KeyValueTreeTransformer()
430     : impl_(new internal::KeyValueTreeTransformerImpl)
431 {
432 }
433
434 KeyValueTreeTransformer::~KeyValueTreeTransformer()
435 {
436 }
437
438 IKeyValueTreeTransformRules *KeyValueTreeTransformer::rules()
439 {
440     return impl_->rootScope_.rules();
441 }
442
443 std::vector<KeyValueTreePath> KeyValueTreeTransformer::mappedPaths() const
444 {
445     std::vector<KeyValueTreePath> result;
446     if (impl_->rootRule_)
447     {
448         impl_->rootRule_->collectMappedPaths(KeyValueTreePath(), &result);
449     }
450     return result;
451 }
452
453 KeyValueTreeTransformResult
454 KeyValueTreeTransformer::transform(const KeyValueTreeObject  &tree,
455                                    IKeyValueTreeErrorHandler *errorHandler) const
456 {
457     internal::KeyValueTreeTransformerImpl::Transformer transformer(errorHandler);
458     transformer.transform(impl_->rootRule_.get(), tree);
459     return transformer.result();
460 }
461
462 /********************************************************************
463  * KeyValueTreeTransformRuleBuilder::Data
464  */
465
466 class KeyValueTreeTransformRuleBuilder::Data
467 {
468     public:
469         typedef internal::KeyValueTreeTransformerImpl::Rule Rule;
470
471         explicit Data(const KeyValueTreePath &prefix)
472             : prefixPath_(prefix), expectedType_(typeid(void)),
473               keyMatchType_(StringCompareType::Exact), keyMatchRule_(false)
474         {
475         }
476
477         void createRule(internal::KeyValueTreeTransformerImpl *impl)
478         {
479             if (keyMatchRule_)
480             {
481                 createRuleWithKeyMatchType(impl);
482                 return;
483             }
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())
488             {
489                 GMX_RELEASE_ASSERT(rule->targetKey_.empty(),
490                                    "Cannot specify multiple rules from a single path");
491                 rule = rule->getOrCreateChildRule(key);
492             }
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_;
499         }
500
501         void createRuleWithKeyMatchType(internal::KeyValueTreeTransformerImpl *impl)
502         {
503             if (fromPath_.empty())
504             {
505                 impl->createRootRule(keyMatchType_);
506             }
507             else
508             {
509                 std::string lastKey = fromPath_.pop_last();
510                 Rule       *rule    = impl->getOrCreateRootRule();
511                 for (const std::string &key : fromPath_.elements())
512                 {
513                     rule = rule->getOrCreateChildRule(key);
514                 }
515                 rule->createChildRule(lastKey, keyMatchType_);
516             }
517         }
518
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_;
526 };
527
528 /********************************************************************
529  * KeyValueTreeTransformRuleBuilder
530  */
531
532 KeyValueTreeTransformRuleBuilder::KeyValueTreeTransformRuleBuilder(
533         internal::KeyValueTreeTransformerImpl *impl, const KeyValueTreePath &prefix)
534     : impl_(impl), data_(new Data(prefix))
535 {
536 }
537
538 KeyValueTreeTransformRuleBuilder::~KeyValueTreeTransformRuleBuilder()
539 {
540     if (!std::uncaught_exception())
541     {
542         data_->createRule(impl_);
543     }
544 }
545
546 void KeyValueTreeTransformRuleBuilder::setFromPath(const KeyValueTreePath &path)
547 {
548     data_->fromPath_ = path;
549 }
550
551 void KeyValueTreeTransformRuleBuilder::setExpectedType(const std::type_index &type)
552 {
553     data_->expectedType_ = type;
554 }
555
556 void KeyValueTreeTransformRuleBuilder::setToPath(const KeyValueTreePath &path)
557 {
558     data_->toPath_ = data_->prefixPath_ + path;
559 }
560
561 void KeyValueTreeTransformRuleBuilder::setKeyMatchType(StringCompareType keyMatchType)
562 {
563     data_->keyMatchType_ = keyMatchType;
564     data_->keyMatchRule_ = true;
565 }
566
567 void KeyValueTreeTransformRuleBuilder::addTransformToAny(
568         const std::function<Any(const Any &)> &transform)
569 {
570     data_->transform_ =
571         [transform] (KeyValueTreeValueBuilder *builder, const KeyValueTreeValue &value)
572         {
573             builder->setAnyValue(transform(value.asAny()));
574         };
575 }
576
577 void KeyValueTreeTransformRuleBuilder::addTransformToObject(
578         const std::function<void(KeyValueTreeObjectBuilder *, const Any &)> &transform)
579 {
580     data_->transform_ =
581         [transform] (KeyValueTreeValueBuilder *builder, const KeyValueTreeValue &value)
582         {
583             KeyValueTreeObjectBuilder obj = builder->createObject();
584             transform(&obj, value.asAny());
585         };
586 }
587
588 } // namespace gmx