More documentation for key-value trees
[gromacs.git] / src / gromacs / utility / keyvaluetree.h
blobb7afb9dc88a1104a5c697159edf07b784952f8dd
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016,2017, 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
36 * \brief
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
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).
60 * \author Teemu Murtola <teemu.murtola@gmail.com>
61 * \inlibraryapi
62 * \ingroup module_utility
64 #ifndef GMX_UTILITY_KEYVALUETREE_H
65 #define GMX_UTILITY_KEYVALUETREE_H
67 #include <algorithm>
68 #include <functional>
69 #include <map>
70 #include <string>
71 #include <utility>
72 #include <vector>
74 #include "gromacs/utility/real.h"
75 #include "gromacs/utility/variant.h"
77 namespace gmx
80 class KeyValueTreeArray;
81 class KeyValueTreeObject;
82 class TextWriter;
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,
99 * either).
101 * \inlibraryapi
102 * \ingroup module_utility
104 class KeyValueTreePath
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);
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 //! Removes the last element in the path, making it the parent path.
117 void pop_back() { return path_.pop_back(); }
118 //! Removes and returns the last element in the path.
119 std::string pop_last()
121 std::string result = std::move(path_.back());
122 path_.pop_back();
123 return result;
126 //! Whether the path is empty (pointing to the root object).
127 bool empty() const { return path_.empty(); }
128 //! Returns the number of elements (=nesting level) in the path.
129 size_t size() const { return path_.size(); }
130 //! Returns the i'th path element.
131 const std::string &operator[](int i) const { return path_[i]; }
132 //! Returns all the path elements.
133 const std::vector<std::string> &elements() const { return path_; }
135 //! Formats the path as a string for display.
136 std::string toString() const;
138 private:
139 std::vector<std::string> path_;
142 class KeyValueTreeValue
144 public:
145 //! Returns whether the value is an array (KeyValueTreeArray).
146 bool isArray() const;
147 //! Returns whether the value is an object (KeyValueTreeObject).
148 bool isObject() const;
149 //! Returns whether the value is of a given type.
150 template <typename T>
151 bool isType() const { return value_.isType<T>(); }
152 //! Returns the type of the value.
153 std::type_index type() const { return value_.type(); }
155 KeyValueTreeArray &asArray();
156 KeyValueTreeObject &asObject();
157 const KeyValueTreeArray &asArray() const;
158 const KeyValueTreeObject &asObject() const;
159 template <typename T>
160 const T &cast() const { return value_.cast<T>(); }
162 //! Returns the raw Variant value (always possible).
163 const Variant &asVariant() const { return value_; }
165 private:
166 explicit KeyValueTreeValue(Variant &&value) : value_(std::move(value)) {}
168 Variant value_;
170 friend class KeyValueTreeBuilder;
171 friend class KeyValueTreeObjectBuilder;
172 friend class KeyValueTreeValueBuilder;
175 class KeyValueTreeArray
177 public:
178 //! Whether all elements of the array are objects.
179 bool isObjectArray() const
181 return std::all_of(values_.begin(), values_.end(),
182 std::mem_fn(&KeyValueTreeValue::isObject));
185 //! Returns the values in the array.
186 const std::vector<KeyValueTreeValue> &values() const { return values_; }
188 private:
189 std::vector<KeyValueTreeValue> values_;
191 friend class KeyValueTreeArrayBuilderBase;
194 class KeyValueTreeProperty
196 public:
197 const std::string &key() const { return value_->first; }
198 const KeyValueTreeValue &value() const { return value_->second; }
200 private:
201 typedef std::map<std::string, KeyValueTreeValue>::const_iterator
202 IteratorType;
204 explicit KeyValueTreeProperty(IteratorType value) : value_(value) {}
206 IteratorType value_;
208 friend class KeyValueTreeObject;
209 friend class KeyValueTreeObjectBuilder;
212 class KeyValueTreeObject
214 public:
215 KeyValueTreeObject() = default;
216 //! Creates a deep copy of an object.
217 KeyValueTreeObject(const KeyValueTreeObject &other)
219 for (const auto &value : other.values_)
221 auto iter = valueMap_.insert(std::make_pair(value.key(), value.value())).first;
222 values_.push_back(KeyValueTreeProperty(iter));
225 //! Assigns a deep copy of an object.
226 KeyValueTreeObject &operator=(KeyValueTreeObject &other)
228 KeyValueTreeObject tmp(other);
229 std::swap(tmp.valueMap_, valueMap_);
230 std::swap(tmp.values_, values_);
231 return *this;
233 //! Default move constructor.
234 KeyValueTreeObject(KeyValueTreeObject &&) = default;
235 //! Default move assignment.
236 KeyValueTreeObject &operator=(KeyValueTreeObject &&) = default;
238 /*! \brief
239 * Returns all properties in the object.
241 * The properties are in the order they were added to the object.
243 const std::vector<KeyValueTreeProperty> &properties() const { return values_; }
245 //! Whether a property with given key exists.
246 bool keyExists(const std::string &key) const
248 return valueMap_.find(key) != valueMap_.end();
250 //! Returns value for a given key.
251 const KeyValueTreeValue &operator[](const std::string &key) const
253 GMX_ASSERT(keyExists(key), "Accessing non-existent value");
254 return valueMap_.at(key);
257 /*! \brief
258 * Returns whether the given object shares any keys with `this`.
260 bool hasDistinctProperties(const KeyValueTreeObject &obj) const;
262 /*! \brief
263 * Writes a string representation of the object with given writer.
265 * The output format is designed to be readable by humans; if some
266 * particular machine-readable format is needed, that should be
267 * implemented outside the generic key-value tree code.
269 void writeUsing(TextWriter *writer) const;
271 private:
272 //! Keeps the properties by key.
273 std::map<std::string, KeyValueTreeValue> valueMap_;
274 //! Keeps the insertion order of properties.
275 std::vector<KeyValueTreeProperty> values_;
277 friend class KeyValueTreeObjectBuilder;
280 /********************************************************************
281 * Inline functions that could not be declared within the classes
284 inline bool KeyValueTreeValue::isArray() const
286 return value_.isType<KeyValueTreeArray>();
288 inline bool KeyValueTreeValue::isObject() const
290 return value_.isType<KeyValueTreeObject>();
292 inline const KeyValueTreeArray &KeyValueTreeValue::asArray() const
294 return value_.cast<KeyValueTreeArray>();
296 inline const KeyValueTreeObject &KeyValueTreeValue::asObject() const
298 return value_.cast<KeyValueTreeObject>();
300 inline KeyValueTreeArray &KeyValueTreeValue::asArray()
302 return value_.castRef<KeyValueTreeArray>();
304 inline KeyValueTreeObject &KeyValueTreeValue::asObject()
306 return value_.castRef<KeyValueTreeObject>();
309 //! \cond libapi
310 /*! \brief
311 * Compares two KeyValueTrees and prints any differences.
313 * \ingroup module_utility
315 void compareKeyValueTrees(TextWriter *writer,
316 const KeyValueTreeObject &tree1,
317 const KeyValueTreeObject &tree2,
318 real ftol,
319 real abstol);
320 //! \endcond
322 } // namespace gmx
324 #endif