2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
35 /*! \libinternal \file
37 * Declares a data structure for JSON-like structured key-value mapping.
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
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).
60 * \author Teemu Murtola <teemu.murtola@gmail.com>
62 * \ingroup module_utility
64 #ifndef GMX_UTILITY_KEYVALUETREE_H
65 #define GMX_UTILITY_KEYVALUETREE_H
74 #include "gromacs/utility/any.h"
75 #include "gromacs/utility/real.h"
80 class KeyValueTreeArray
;
81 class KeyValueTreeObject
;
84 /*! \libinternal \brief
85 * Identifies an entry in a key-value tree.
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.
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,
102 * \ingroup module_utility
104 class KeyValueTreePath
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
);
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
)
119 auto elements
= other
.elements();
120 path_
.insert(path_
.end(), elements
.begin(), elements
.end());
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()
127 std::string result
= std::move(path_
.back());
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_
; }
141 //! Formats the path as a string for display.
142 std::string
toString() const;
145 std::vector
<std::string
> path_
;
150 //! Combines two paths as with KeyValueTreePath::append().
151 inline KeyValueTreePath
operator+(const KeyValueTreePath
& a
, const KeyValueTreePath
& b
)
153 KeyValueTreePath
result(a
);
158 //! Combines an element to a path as with KeyValueTreePath::append().
159 inline KeyValueTreePath
operator+(const KeyValueTreePath
& a
, const std::string
& b
)
161 KeyValueTreePath
result(a
);
167 class KeyValueTreeValue
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.
178 return value_
.isType
<T
>();
180 //! Returns the type of the value.
181 std::type_index
type() const { return value_
.type(); }
183 KeyValueTreeArray
& asArray();
184 KeyValueTreeObject
& asObject();
185 const KeyValueTreeArray
& asArray() const;
186 const KeyValueTreeObject
& asObject() const;
188 const T
& cast() const
190 return value_
.cast
<T
>();
193 //! Returns the raw Any value (always possible).
194 const Any
& asAny() const { return value_
; }
197 explicit KeyValueTreeValue(Any
&& value
) : value_(std::move(value
)) {}
201 friend class KeyValueTreeBuilder
;
202 friend class KeyValueTreeObjectBuilder
;
203 friend class KeyValueTreeValueBuilder
;
206 class KeyValueTreeArray
209 //! Whether all elements of the array are objects.
210 bool isObjectArray() const
212 return std::all_of(values_
.begin(), values_
.end(), std::mem_fn(&KeyValueTreeValue::isObject
));
215 //! Returns the values in the array.
216 const std::vector
<KeyValueTreeValue
>& values() const { return values_
; }
219 std::vector
<KeyValueTreeValue
> values_
;
221 friend class KeyValueTreeArrayBuilderBase
;
224 class KeyValueTreeProperty
227 const std::string
& key() const { return value_
->first
; }
228 const KeyValueTreeValue
& value() const { return value_
->second
; }
231 typedef std::map
<std::string
, KeyValueTreeValue
>::const_iterator IteratorType
;
233 explicit KeyValueTreeProperty(IteratorType value
) : value_(value
) {}
237 friend class KeyValueTreeObject
;
238 friend class KeyValueTreeObjectBuilder
;
241 class KeyValueTreeObject
244 KeyValueTreeObject() = default;
245 //! Creates a deep copy of an object.
246 KeyValueTreeObject(const KeyValueTreeObject
& other
)
248 for (const auto& value
: other
.values_
)
250 auto iter
= valueMap_
.insert(std::make_pair(value
.key(), value
.value())).first
;
251 values_
.push_back(KeyValueTreeProperty(iter
));
254 //! Assigns a deep copy of an object.
255 KeyValueTreeObject
& operator=(const KeyValueTreeObject
& other
)
257 KeyValueTreeObject
tmp(other
);
258 std::swap(tmp
.valueMap_
, valueMap_
);
259 std::swap(tmp
.values_
, values_
);
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;
269 * Returns all properties in the object.
271 * The properties are in the order they were added to the object.
273 const std::vector
<KeyValueTreeProperty
>& properties() const { return values_
; }
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
280 GMX_ASSERT(keyExists(key
), "Accessing non-existent value");
281 return valueMap_
.at(key
);
285 * Returns whether the given object shares any keys with `this`.
287 bool hasDistinctProperties(const KeyValueTreeObject
& obj
) const;
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_
;
295 friend class KeyValueTreeObjectBuilder
;
298 /********************************************************************
299 * Inline functions that could not be declared within the classes
302 inline bool KeyValueTreeValue::isArray() const
304 return value_
.isType
<KeyValueTreeArray
>();
306 inline bool KeyValueTreeValue::isObject() const
308 return value_
.isType
<KeyValueTreeObject
>();
310 inline const KeyValueTreeArray
& KeyValueTreeValue::asArray() const
312 return value_
.cast
<KeyValueTreeArray
>();
314 inline const KeyValueTreeObject
& KeyValueTreeValue::asObject() const
316 return value_
.cast
<KeyValueTreeObject
>();
318 inline KeyValueTreeArray
& KeyValueTreeValue::asArray()
320 return value_
.castRef
<KeyValueTreeArray
>();
322 inline KeyValueTreeObject
& KeyValueTreeValue::asObject()
324 return value_
.castRef
<KeyValueTreeObject
>();
329 * Writes a human-readable representation of the tree with given writer.
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.
335 * \ingroup module_utility
337 void dumpKeyValueTree(TextWriter
* writer
, const KeyValueTreeObject
& tree
);
340 * Compares two KeyValueTrees and prints any differences.
342 * \ingroup module_utility
344 void compareKeyValueTrees(TextWriter
* writer
,
345 const KeyValueTreeObject
& tree1
,
346 const KeyValueTreeObject
& tree2
,
350 //! Helper function to format a simple KeyValueTreeValue.
351 static inline std::string
simpleValueToString(const KeyValueTreeValue
& value
)
353 return simpleValueToString(value
.asAny());