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