Improve MessageStringCollector
[alexxy/gromacs.git] / src / gromacs / utility / keyvaluetree.h
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 /*! \libinternal \file
36  * \brief
37  * Declares a data structure for JSON-like structured key-value mapping.
38  *
39  * A tree is composed of nodes that can have different types:
40  *  - _Value_ (gmx::KeyValueTreeValue) is a generic node that can
41  *    represent either a scalar value of arbitrary type, or an object or
42  *    an array.
43  *  - _Array_ (gmx::KeyValueTreeArray) is a collection of any number of values
44  *    (including zero).  The values can be of any type and different types
45  *    can be mixed in the same array.
46  *  - _Object_ (gmx::KeyValueTreeObject) is a collection of properties.
47  *    Each property must have a unique key.  Order of properties is preserved,
48  *    i.e., they can be iterated in the order they were added.
49  *  - _Property_ (gmx::KeyValueTreeProperty) is an arbitrary type of value
50  *    associated with a string key.
51  * The root object of a tree is typically an object, but could also be an
52  * array.  The data structure itself does not enforce any other constraints,
53  * but the context in which it is used can limit the allowed scalar types or,
54  * e.g., require arrays to have values of uniform type.  Also, several
55  * operations defined for the structure (string output, comparison,
56  * serialization, etc.) only work on a limited set of scalar types, or have
57  * limitations with the types of trees they work on (in particular, arrays are
58  * currently poorly supported).
59  *
60  * \author Teemu Murtola <teemu.murtola@gmail.com>
61  * \inlibraryapi
62  * \ingroup module_utility
63  */
64 #ifndef GMX_UTILITY_KEYVALUETREE_H
65 #define GMX_UTILITY_KEYVALUETREE_H
66
67 #include <algorithm>
68 #include <functional>
69 #include <map>
70 #include <string>
71 #include <utility>
72 #include <vector>
73
74 #include "gromacs/utility/any.h"
75 #include "gromacs/utility/real.h"
76
77 namespace gmx
78 {
79
80 class KeyValueTreeArray;
81 class KeyValueTreeObject;
82 class TextWriter;
83
84 /*! \libinternal \brief
85  * Identifies an entry in a key-value tree.
86  *
87  * This class is mainly an internal utility within the key-value tree
88  * implementation, but it is exposed on the API level where string-based
89  * specification of a location in the tree is necessary.  Constructors are not
90  * explicit to allow passing a simple string in contexts where a
91  * KeyValueTreePath is expected.
92  *
93  * The string specifying a location should start with a `/`, followed by the
94  * names of the properties separated by `/`.  For example, `/a/b/c` specifies
95  * property `c` in an object that is the value of `b` in an object that is the
96  * value of `a` at the root of the tree.
97  * Currently, there is no support for specifying paths to values within arrays
98  * (since none of the places where this is used implement array handling,
99  * either).
100  *
101  * \inlibraryapi
102  * \ingroup module_utility
103  */
104 class KeyValueTreePath
105 {
106 public:
107     //! Creates an empty path (corresponds to the root object).
108     KeyValueTreePath() = default;
109     //! Creates a path from given string representation.
110     KeyValueTreePath(const char* path);
111     //! Creates a path from given string representation.
112     KeyValueTreePath(const std::string& path);
113
114     //! Adds another element to the path, making it a child of the old path.
115     void append(const std::string& key) { path_.push_back(key); }
116     //! Adds elements from another path to the path.
117     void append(const KeyValueTreePath& other)
118     {
119         auto elements = other.elements();
120         path_.insert(path_.end(), elements.begin(), elements.end());
121     }
122     //! Removes the last element in the path, making it the parent path.
123     void pop_back() { return path_.pop_back(); }
124     //! Removes and returns the last element in the path.
125     std::string pop_last()
126     {
127         std::string result = std::move(path_.back());
128         path_.pop_back();
129         return result;
130     }
131
132     //! Whether the path is empty (pointing to the root object).
133     bool empty() const { return path_.empty(); }
134     //! Returns the number of elements (=nesting level) in the path.
135     size_t size() const { return path_.size(); }
136     //! Returns the i'th path element.
137     const std::string& operator[](int i) const { return path_[i]; }
138     //! Returns all the path elements.
139     const std::vector<std::string>& elements() const { return path_; }
140
141     //! Formats the path as a string for display.
142     std::string toString() const;
143
144 private:
145     std::vector<std::string> path_;
146 };
147
148 //! \cond libapi
149
150 //! Combines two paths as with KeyValueTreePath::append().
151 inline KeyValueTreePath operator+(const KeyValueTreePath& a, const KeyValueTreePath& b)
152 {
153     KeyValueTreePath result(a);
154     result.append(b);
155     return result;
156 }
157
158 //! Combines an element to a path as with KeyValueTreePath::append().
159 inline KeyValueTreePath operator+(const KeyValueTreePath& a, const std::string& b)
160 {
161     KeyValueTreePath result(a);
162     result.append(b);
163     return result;
164 }
165 //! \endcond
166
167 class KeyValueTreeValue
168 {
169 public:
170     //! Returns whether the value is an array (KeyValueTreeArray).
171     bool isArray() const;
172     //! Returns whether the value is an object (KeyValueTreeObject).
173     bool isObject() const;
174     //! Returns whether the value is of a given type.
175     template<typename T>
176     bool isType() const
177     {
178         return value_.isType<T>();
179     }
180     //! Returns the type of the value.
181     std::type_index type() const { return value_.type(); }
182
183     KeyValueTreeArray&        asArray();
184     KeyValueTreeObject&       asObject();
185     const KeyValueTreeArray&  asArray() const;
186     const KeyValueTreeObject& asObject() const;
187     template<typename T>
188     const T& cast() const
189     {
190         return value_.cast<T>();
191     }
192
193     //! Returns the raw Any value (always possible).
194     const Any& asAny() const { return value_; }
195
196 private:
197     explicit KeyValueTreeValue(Any&& value) : value_(std::move(value)) {}
198
199     Any value_;
200
201     friend class KeyValueTreeBuilder;
202     friend class KeyValueTreeObjectBuilder;
203     friend class KeyValueTreeValueBuilder;
204 };
205
206 class KeyValueTreeArray
207 {
208 public:
209     //! Whether all elements of the array are objects.
210     bool isObjectArray() const
211     {
212         return std::all_of(values_.begin(), values_.end(), std::mem_fn(&KeyValueTreeValue::isObject));
213     }
214
215     //! Returns the values in the array.
216     const std::vector<KeyValueTreeValue>& values() const { return values_; }
217
218 private:
219     std::vector<KeyValueTreeValue> values_;
220
221     friend class KeyValueTreeArrayBuilderBase;
222 };
223
224 class KeyValueTreeProperty
225 {
226 public:
227     const std::string&       key() const { return value_->first; }
228     const KeyValueTreeValue& value() const { return value_->second; }
229
230 private:
231     typedef std::map<std::string, KeyValueTreeValue>::const_iterator IteratorType;
232
233     explicit KeyValueTreeProperty(IteratorType value) : value_(value) {}
234
235     IteratorType value_;
236
237     friend class KeyValueTreeObject;
238     friend class KeyValueTreeObjectBuilder;
239 };
240
241 class KeyValueTreeObject
242 {
243 public:
244     KeyValueTreeObject() = default;
245     //! Creates a deep copy of an object.
246     KeyValueTreeObject(const KeyValueTreeObject& other)
247     {
248         for (const auto& value : other.values_)
249         {
250             auto iter = valueMap_.insert(std::make_pair(value.key(), value.value())).first;
251             values_.push_back(KeyValueTreeProperty(iter));
252         }
253     }
254     //! Assigns a deep copy of an object.
255     KeyValueTreeObject& operator=(const KeyValueTreeObject& other)
256     {
257         KeyValueTreeObject tmp(other);
258         std::swap(tmp.valueMap_, valueMap_);
259         std::swap(tmp.values_, values_);
260         return *this;
261     }
262     //! Default move constructor.
263     //NOLINTNEXTLINE(performance-noexcept-move-constructor) bug #38733
264     KeyValueTreeObject(KeyValueTreeObject&&) = default;
265     //! Default move assignment.
266     KeyValueTreeObject& operator=(KeyValueTreeObject&&) = default;
267
268     /*! \brief
269      * Returns all properties in the object.
270      *
271      * The properties are in the order they were added to the object.
272      */
273     const std::vector<KeyValueTreeProperty>& properties() const { return values_; }
274
275     //! Whether a property with given key exists.
276     bool keyExists(const std::string& key) const { return valueMap_.find(key) != valueMap_.end(); }
277     //! Returns value for a given key.
278     const KeyValueTreeValue& operator[](const std::string& key) const
279     {
280         GMX_ASSERT(keyExists(key), "Accessing non-existent value");
281         return valueMap_.at(key);
282     }
283
284     /*! \brief
285      * Returns whether the given object shares any keys with `this`.
286      */
287     bool hasDistinctProperties(const KeyValueTreeObject& obj) const;
288
289 private:
290     //! Keeps the properties by key.
291     std::map<std::string, KeyValueTreeValue> valueMap_;
292     //! Keeps the insertion order of properties.
293     std::vector<KeyValueTreeProperty> values_;
294
295     friend class KeyValueTreeObjectBuilder;
296 };
297
298 /********************************************************************
299  * Inline functions that could not be declared within the classes
300  */
301
302 inline bool KeyValueTreeValue::isArray() const
303 {
304     return value_.isType<KeyValueTreeArray>();
305 }
306 inline bool KeyValueTreeValue::isObject() const
307 {
308     return value_.isType<KeyValueTreeObject>();
309 }
310 inline const KeyValueTreeArray& KeyValueTreeValue::asArray() const
311 {
312     return value_.cast<KeyValueTreeArray>();
313 }
314 inline const KeyValueTreeObject& KeyValueTreeValue::asObject() const
315 {
316     return value_.cast<KeyValueTreeObject>();
317 }
318 inline KeyValueTreeArray& KeyValueTreeValue::asArray()
319 {
320     return value_.castRef<KeyValueTreeArray>();
321 }
322 inline KeyValueTreeObject& KeyValueTreeValue::asObject()
323 {
324     return value_.castRef<KeyValueTreeObject>();
325 }
326
327 //! \cond libapi
328 /*! \brief
329  * Writes a human-readable representation of the tree with given writer.
330  *
331  * The output format is designed to be readable by humans; if some
332  * particular machine-readable format is needed, that should be
333  * implemented outside the generic key-value tree code.
334  *
335  * \ingroup module_utility
336  */
337 void dumpKeyValueTree(TextWriter* writer, const KeyValueTreeObject& tree);
338
339 /*! \brief
340  * Compares two KeyValueTrees and prints any differences.
341  *
342  * \ingroup module_utility
343  */
344 void compareKeyValueTrees(TextWriter*               writer,
345                           const KeyValueTreeObject& tree1,
346                           const KeyValueTreeObject& tree2,
347                           real                      ftol,
348                           real                      abstol);
349
350 //! Helper function to format a simple KeyValueTreeValue.
351 static inline std::string simpleValueToString(const KeyValueTreeValue& value)
352 {
353     return simpleValueToString(value.asAny());
354 }
355
356 //! \endcond
357
358 } // namespace gmx
359
360 #endif