Improve MessageStringCollector
[alexxy/gromacs.git] / src / gromacs / utility / keyvaluetreetransform.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2016,2017,2018,2019,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 /*! \libinternal \file
36  * \brief
37  * Declares utilities for transforming key-value trees.
38  *
39  * See \ref page_mdmodules for the main use case that these support.
40  *
41  * \author Teemu Murtola <teemu.murtola@gmail.com>
42  * \inlibraryapi
43  * \ingroup module_utility
44  */
45 #ifndef GMX_UTILITY_KEYVALUETREETRANSFORM_H
46 #define GMX_UTILITY_KEYVALUETREETRANSFORM_H
47
48 #include <functional>
49 #include <memory>
50 #include <string>
51 #include <typeindex>
52 #include <vector>
53
54 #include "gromacs/utility/any.h"
55 #include "gromacs/utility/keyvaluetree.h"
56
57 namespace gmx
58 {
59
60 class IKeyValueTreeErrorHandler;
61 class KeyValueTreeObjectBuilder;
62
63 enum class StringCompareType;
64
65 class KeyValueTreeTransformResult;
66 class KeyValueTreeTransformRuleBuilder;
67 class KeyValueTreeTransformRulesScoped;
68
69 namespace internal
70 {
71 class KeyValueTreeTransformerImpl;
72 }
73
74 /*! \libinternal \brief
75  * Interface to declare rules for transforming key-value trees.
76  *
77  * This interface is used to add transformation rules for key-value trees.
78  * A transformation is a set of rules that is used to map an input key-value
79  * tree to an output key-value tree, with possible conversion steps performed
80  * in the process.  Currently, each rule maps one item from the source tree to
81  * one item in the target tree (it is possible to expand a single value into an
82  * object with multiple properties).  See KeyValueTreeTransformRuleBuilder for
83  * the kinds of rules currently supported.
84  *
85  * The main use currently is in converting flat-format mdp files to a
86  * structured internal representation.
87  *
88  * \inlibraryapi
89  * \ingroup module_utility
90  */
91 class IKeyValueTreeTransformRules
92 {
93 public:
94     /*! \brief
95      * Creates a new rule.
96      *
97      * Properties of the new rule must be specified using the returned
98      * builder.
99      */
100     virtual KeyValueTreeTransformRuleBuilder addRule() = 0;
101     /*! \brief
102      * Creates a scoped set of rules, where all rules use a target sub-tree.
103      *
104      * \param[in] scope Prefix defining the scope in the target tree
105      *
106      * Any rules added to the returned scope will have `scope` prefixed to
107      * their target paths, i.e., it is not possible to produce elements
108      * outside the specified subtree.
109      */
110     virtual KeyValueTreeTransformRulesScoped scopedTransform(const KeyValueTreePath& scope) = 0;
111
112 protected:
113     virtual ~IKeyValueTreeTransformRules();
114 };
115
116 /*! \libinternal \brief
117  * Helper object returned from IKeyValueTreeTransformRules::scopedTransform().
118  *
119  * \inlibraryapi
120  * \ingroup module_utility
121  */
122 class KeyValueTreeTransformRulesScoped
123 {
124 public:
125     //! Internal constructor for creating the scope.
126     KeyValueTreeTransformRulesScoped(internal::KeyValueTreeTransformerImpl* impl,
127                                      const KeyValueTreePath&                prefix);
128     //! Supports returning the object from IKeyValueTreeTransformRules::scopedTransform().
129     KeyValueTreeTransformRulesScoped(KeyValueTreeTransformRulesScoped&& other) noexcept;
130     //! Supports returning the object from IKeyValueTreeTransformRules::scopedTransform().
131     KeyValueTreeTransformRulesScoped& operator=(KeyValueTreeTransformRulesScoped&& other) noexcept;
132     ~KeyValueTreeTransformRulesScoped();
133
134     //! Returns the interface for adding rules to this scope.
135     IKeyValueTreeTransformRules* rules();
136
137 private:
138     class Impl;
139
140     std::unique_ptr<Impl> impl_;
141 };
142
143 /*! \libinternal \brief
144  * Provides methods to specify one transformation rule.
145  *
146  * \if internal
147  * The builder is implemented as a set of nested objects, each of which is
148  * provides methods for setting a particular property of the rule.  Setting a
149  * property returns another object that has relevant methods for the context.
150  * This provides some structure to the methods, and catches at least some types
151  * of incorrect rules already at compile time.
152  * Additionally, if you use an IDE with completion facilities, it can nicely
153  * guide you through which values you need to specify.
154  * All values are stored within the main builder object, and the rule is
155  * created at the end of the statement.
156  * \endif
157  *
158  * \inlibraryapi
159  * \ingroup module_utility
160  */
161 class KeyValueTreeTransformRuleBuilder
162 {
163 public:
164     /*! \internal \brief
165      * Base class used for implementing parameter provider objects.
166      */
167     class Base
168     {
169     protected:
170         //! Creates a parameter provider object within given builder.
171         explicit Base(KeyValueTreeTransformRuleBuilder* builder) : builder_(builder) {}
172
173         //! The parent builder.
174         KeyValueTreeTransformRuleBuilder* builder_;
175     };
176
177     /*! \libinternal \brief
178      * Properties that can be specified after from().to().
179      *
180      * \tparam FromType Type specified for from() to map from.
181      * \tparam ToType Type specified for to() to map to.
182      */
183     template<typename FromType, typename ToType>
184     class ToValue : public Base
185     {
186     public:
187         //! Creates a parameter provider object within given builder.
188         explicit ToValue(KeyValueTreeTransformRuleBuilder* builder) : Base(builder) {}
189
190         /*! \brief
191          * Specifies the transformation function to convert the value
192          * from FromType to ToType.
193          */
194         void transformWith(std::function<ToType(const FromType&)> transform)
195         {
196             builder_->addTransformToAny([transform](const Any& value) {
197                 return Any::create<ToType>(transform(value.cast<FromType>()));
198             });
199         }
200     };
201
202     /*! \libinternal \brief
203      * Properties that can be specified after from().toObject().
204      *
205      * \tparam FromType Type specified for from() to map from.
206      */
207     template<typename FromType>
208     class ToObject : public Base
209     {
210     public:
211         //! Creates a parameter provider object within given builder.
212         explicit ToObject(KeyValueTreeTransformRuleBuilder* builder) : Base(builder) {}
213
214         /*! \brief
215          * Specifies the transformation function to build the output
216          * object.
217          *
218          * The transform should build the output object with the
219          * provided builder.
220          */
221         void transformWith(std::function<void(KeyValueTreeObjectBuilder*, const FromType&)> transform)
222         {
223             builder_->addTransformToObject([transform](KeyValueTreeObjectBuilder* builder, const Any& value) {
224                 transform(builder, value.cast<FromType>());
225             });
226         }
227     };
228
229     /*! \libinternal \brief
230      * Properties that can be specified after from().
231      *
232      * \tparam FromType Type specified for from() to map from.
233      */
234     template<typename FromType>
235     class AfterFrom : public Base
236     {
237     public:
238         //! Creates a parameter provider object within given builder.
239         explicit AfterFrom(KeyValueTreeTransformRuleBuilder* builder) : Base(builder) {}
240
241         /*! \brief
242          * Specifies a rule that maps to a value at given path.
243          *
244          * \tparam ToType  Type to map to.
245          * \param[in] path Path to map to.
246          *
247          * It is an error if multiple rules map to the same path, or to
248          * a parent path of the target of an existing rule.
249          * Note that it is possible to have a to() rule map to a child
250          * of a toObject() rule, provided that the path is not created
251          * by the object rule.
252          */
253         template<typename ToType>
254         ToValue<FromType, ToType> to(const KeyValueTreePath& path)
255         {
256             builder_->setToPath(path);
257             return ToValue<FromType, ToType>(builder_);
258         }
259
260         /*! \brief
261          * Specifies a rule that maps to an object (collection of named
262          * values) at given path.
263          *
264          * \param[in] path Path to map to.
265          *
266          * It is an error if multiple rules map to the same path, or to
267          * a parent path of the target of an existing rule.
268          * However, it is allowed to have two toObject() rules map to
269          * the same path, provided that the properties they produce are
270          * distinct.
271          */
272         ToObject<FromType> toObject(const KeyValueTreePath& path)
273         {
274             builder_->setToPath(path);
275             return ToObject<FromType>(builder_);
276         }
277     };
278
279     //! Internal constructor for creating a builder.
280     KeyValueTreeTransformRuleBuilder(internal::KeyValueTreeTransformerImpl* impl,
281                                      const KeyValueTreePath&                prefix);
282     //! Supports returning the builder from IKeyValueTreeTransformRules::addRule().
283     KeyValueTreeTransformRuleBuilder(KeyValueTreeTransformRuleBuilder&&) = default;
284     //! Supports returning the builder from IKeyValueTreeTransformRules::addRule().
285     KeyValueTreeTransformRuleBuilder& operator=(KeyValueTreeTransformRuleBuilder&&) = default;
286     ~KeyValueTreeTransformRuleBuilder(); // NOLINT(bugprone-exception-escape)
287
288     /*! \brief
289      * Specifies a rule that maps a value at given path.
290      *
291      * \tparam FromType Type of value expected at `path`.
292      * \param[in] path Path to map in this rule.
293      *
294      * If the input tree has `path`, but it is not of type `FromType`,
295      * the transform will produce an error.
296      *
297      * It is an error to use the same path in two from() rules.  Similarly,
298      * it is an error to use a child path of a path used in a different
299      * from() rule.
300      */
301     template<typename FromType>
302     AfterFrom<FromType> from(const KeyValueTreePath& path)
303     {
304         setFromPath(path);
305         setExpectedType(typeid(FromType));
306         return AfterFrom<FromType>(this);
307     }
308     /*! \brief
309      * Specifies how strings are matched when matching rules against a path.
310      *
311      * For properties of the object at `path`, `keyMatchType` is used for
312      * string comparison.
313      *
314      * This rule must be specified first for a path, before any other
315      * from() rule specifies the path or a subpath.
316      * The rule only applies to immediate properties at the given path, not
317      * recursively.
318      * It is an error to specify the match type multiple times for a path.
319      */
320     void keyMatchType(const KeyValueTreePath& path, StringCompareType keyMatchType)
321     {
322         setFromPath(path);
323         setKeyMatchType(keyMatchType);
324     }
325
326 private:
327     void setFromPath(const KeyValueTreePath& path);
328     void setExpectedType(const std::type_index& type);
329     void setToPath(const KeyValueTreePath& path);
330     void setKeyMatchType(StringCompareType keyMatchType);
331     void addTransformToAny(const std::function<Any(const Any&)>& transform);
332     void addTransformToObject(const std::function<void(KeyValueTreeObjectBuilder*, const Any&)>& transform);
333
334     class Data;
335
336     internal::KeyValueTreeTransformerImpl* impl_;
337     std::unique_ptr<Data>                  data_;
338 };
339
340 class KeyValueTreeTransformer
341 {
342 public:
343     KeyValueTreeTransformer();
344     ~KeyValueTreeTransformer();
345
346     IKeyValueTreeTransformRules* rules();
347
348     std::vector<KeyValueTreePath> mappedPaths() const;
349
350     KeyValueTreeTransformResult transform(const KeyValueTreeObject&  tree,
351                                           IKeyValueTreeErrorHandler* errorHandler) const;
352
353 private:
354     std::unique_ptr<internal::KeyValueTreeTransformerImpl> impl_;
355 };
356
357 class IKeyValueTreeBackMapping
358 {
359 public:
360     virtual ~IKeyValueTreeBackMapping();
361
362     virtual KeyValueTreePath originalPath(const KeyValueTreePath& path) const = 0;
363 };
364
365 class KeyValueTreeTransformResult
366 {
367 public:
368     KeyValueTreeObject              object() { return std::move(object_); }
369     const IKeyValueTreeBackMapping& backMapping() const { return *mapping_; }
370
371 private:
372     typedef std::unique_ptr<IKeyValueTreeBackMapping> MappingPointer;
373
374     KeyValueTreeTransformResult(KeyValueTreeObject&& object, MappingPointer&& mapping) :
375         object_(std::move(object)), mapping_(std::move(mapping))
376     {
377     }
378
379     KeyValueTreeObject object_;
380     MappingPointer     mapping_;
381
382     friend class internal::KeyValueTreeTransformerImpl;
383 };
384
385 } // namespace gmx
386
387 #endif