2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016,2017,2018, 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.
37 #include "keyvaluetree.h"
42 #include "gromacs/utility/compare.h"
43 #include "gromacs/utility/gmxassert.h"
44 #include "gromacs/utility/strconvert.h"
45 #include "gromacs/utility/stringutil.h"
46 #include "gromacs/utility/textwriter.h"
54 //! Helper function to split a KeyValueTreePath to its components
55 std::vector
<std::string
> splitPathElements(const std::string
&path
)
57 GMX_ASSERT(!path
.empty() && path
[0] == '/',
58 "Paths to KeyValueTree should start with '/'");
59 return splitDelimitedString(path
.substr(1), '/');
64 /********************************************************************
68 KeyValueTreePath::KeyValueTreePath(const char *path
)
69 : path_(splitPathElements(path
))
73 KeyValueTreePath::KeyValueTreePath(const std::string
&path
)
74 : path_(splitPathElements(path
))
78 std::string
KeyValueTreePath::toString() const
80 return "/" + joinStrings(path_
, "/");
83 /********************************************************************
87 bool KeyValueTreeObject::hasDistinctProperties(const KeyValueTreeObject
&obj
) const
89 for (const auto &prop
: obj
.values_
)
91 if (keyExists(prop
.key()))
93 GMX_RELEASE_ASSERT(!prop
.value().isArray(),
94 "Comparison of arrays not implemented");
95 if (prop
.value().isObject() && valueMap_
.at(prop
.key()).isObject())
97 return valueMap_
.at(prop
.key()).asObject().hasDistinctProperties(prop
.value().asObject());
105 /********************************************************************
106 * Key value tree dump
110 void dumpKeyValueTree(TextWriter
*writer
, const KeyValueTreeObject
&tree
)
112 for (const auto &prop
: tree
.properties())
114 const auto &value
= prop
.value();
115 if (value
.isObject())
117 writer
->writeString(prop
.key());
118 writer
->writeLine(":");
119 int oldIndent
= writer
->wrapperSettings().indent();
120 writer
->wrapperSettings().setIndent(oldIndent
+ 2);
121 dumpKeyValueTree(writer
, value
.asObject());
122 writer
->wrapperSettings().setIndent(oldIndent
);
126 int indent
= writer
->wrapperSettings().indent();
127 writer
->writeString(formatString("%*s", -(33-indent
), prop
.key().c_str()));
128 writer
->writeString(" = ");
131 writer
->writeString("[");
132 for (const auto &elem
: value
.asArray().values())
134 GMX_RELEASE_ASSERT(!elem
.isObject() && !elem
.isArray(),
135 "Arrays of objects not currently implemented");
136 writer
->writeString(" ");
137 writer
->writeString(simpleValueToString(elem
));
139 writer
->writeString(" ]");
143 writer
->writeString(simpleValueToString(value
));
151 /********************************************************************
152 * Key value tree comparison
161 CompareHelper(TextWriter
*writer
, real ftol
, real abstol
)
162 : writer_(writer
), ftol_(ftol
), abstol_(abstol
)
166 void compareObjects(const KeyValueTreeObject
&obj1
,
167 const KeyValueTreeObject
&obj2
)
169 for (const auto &prop1
: obj1
.properties())
171 currentPath_
.append(prop1
.key());
172 if (obj2
.keyExists(prop1
.key()))
174 compareValues(prop1
.value(), obj2
[prop1
.key()]);
178 handleMissingKeyInSecondObject(prop1
.value());
180 currentPath_
.pop_back();
182 for (const auto &prop2
: obj2
.properties())
184 currentPath_
.append(prop2
.key());
185 if (!obj1
.keyExists(prop2
.key()))
187 handleMissingKeyInFirstObject(prop2
.value());
189 currentPath_
.pop_back();
194 void compareValues(const KeyValueTreeValue
&value1
,
195 const KeyValueTreeValue
&value2
)
197 if (value1
.type() == value2
.type())
199 if (value1
.isObject())
201 compareObjects(value1
.asObject(), value2
.asObject());
203 else if (value1
.isArray())
205 GMX_RELEASE_ASSERT(false, "Array comparison not implemented");
207 else if (!areSimpleValuesOfSameTypeEqual(value1
, value2
))
209 writer_
->writeString(currentPath_
.toString());
210 writer_
->writeLine(formatString(" (%s - %s)", simpleValueToString(value1
).c_str(), simpleValueToString(value2
).c_str()));
213 else if ((value1
.isType
<double>() && value2
.isType
<float>())
214 || (value1
.isType
<float>() && value2
.isType
<double>()))
216 const bool firstIsDouble
217 = value1
.isType
<double>();
218 const float v1
= firstIsDouble
? value1
.cast
<double>() : value1
.cast
<float>();
219 const float v2
= firstIsDouble
? value2
.cast
<float>() : value2
.cast
<double>();
220 if (!equal_float(v1
, v2
, ftol_
, abstol_
))
222 writer_
->writeString(currentPath_
.toString());
223 writer_
->writeLine(formatString(" (%e - %e)", v1
, v2
));
228 handleMismatchingTypes(value1
, value2
);
232 bool areSimpleValuesOfSameTypeEqual(
233 const KeyValueTreeValue
&value1
,
234 const KeyValueTreeValue
&value2
)
236 GMX_ASSERT(value1
.type() == value2
.type(),
237 "Caller should ensure that types are equal");
238 if (value1
.isType
<bool>())
240 return value1
.cast
<bool>() == value2
.cast
<bool>();
242 else if (value1
.isType
<int>())
244 return value1
.cast
<int>() == value2
.cast
<int>();
246 else if (value1
.isType
<int64_t>())
248 return value1
.cast
<int64_t>() == value2
.cast
<int64_t>();
250 else if (value1
.isType
<double>())
252 return equal_double(value1
.cast
<double>(), value2
.cast
<double>(), ftol_
, abstol_
);
254 else if (value1
.isType
<float>())
256 return equal_float(value1
.cast
<float>(), value2
.cast
<float>(), ftol_
, abstol_
);
258 else if (value1
.isType
<std::string
>())
260 return value1
.cast
<std::string
>() == value2
.cast
<std::string
>();
264 GMX_RELEASE_ASSERT(false, "Unknown value type");
269 void handleMismatchingTypes(const KeyValueTreeValue
& /* value1 */,
270 const KeyValueTreeValue
& /* value2 */)
272 writer_
->writeString(currentPath_
.toString());
273 writer_
->writeString(" type mismatch");
276 void handleMissingKeyInFirstObject(const KeyValueTreeValue
&value
)
278 const std::string message
= formatString(
279 "%s (missing - %s)", currentPath_
.toString().c_str(),
280 formatValueForMissingMessage(value
).c_str());
281 writer_
->writeLine(message
);
283 void handleMissingKeyInSecondObject(const KeyValueTreeValue
&value
)
285 const std::string message
= formatString(
286 "%s (%s - missing)", currentPath_
.toString().c_str(),
287 formatValueForMissingMessage(value
).c_str());
288 writer_
->writeLine(message
);
291 std::string
formatValueForMissingMessage(const KeyValueTreeValue
&value
)
293 if (value
.isObject() || value
.isArray())
297 return simpleValueToString(value
);
300 KeyValueTreePath currentPath_
;
309 void compareKeyValueTrees(TextWriter
*writer
,
310 const KeyValueTreeObject
&tree1
,
311 const KeyValueTreeObject
&tree2
,
315 CompareHelper
helper(writer
, ftol
, abstol
);
316 helper
.compareObjects(tree1
, tree2
);