forked from npshub/mantid
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PropertyWithValueJSON.cpp
163 lines (144 loc) · 6.48 KB
/
PropertyWithValueJSON.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2007 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidKernel/PropertyWithValueJSON.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/OptionalBool.h"
#include "MantidKernel/PropertyManager.h"
#include "MantidKernel/PropertyManagerProperty.h"
#include <json/value.h>
#include <map>
using Json::Value;
namespace Mantid::Kernel {
namespace {
// A pointer to a member function for doing the Json::Value->C++ type conversion
// Used only for type deduction in the FromJson constructor
template <typename T> using ValueAsTypeMemFn = T (Json::Value::*)() const;
// A non-templated outer struct that can be stored in a container without
// requiring a pointer. The implementation follows the concept-model idiom
// of storing the templated type using type erasure. Creating an object
// of this type uses the type passed to the constructor to infer the
// template parameter type. This template type is then used to call
// the appropriate ToCpp conversion function defined in the header when
// createProperty is called.
struct FromJson {
template <typename T> explicit FromJson(ValueAsTypeMemFn<T> /*unused*/) : m_self{std::make_unique<ModelT<T>>()} {}
std::unique_ptr<Property> createProperty(const std::string &name, const Json::Value &value, bool createArray) const {
if (createArray)
return m_self->arrayValueProperty(name, value);
else
return m_self->singleValueProperty(name, value);
}
private:
struct ConceptT {
virtual ~ConceptT() = default;
virtual std::unique_ptr<Property> singleValueProperty(const std::string &name, const Json::Value &value) const = 0;
virtual std::unique_ptr<Property> arrayValueProperty(const std::string &name, const Json::Value &value) const = 0;
};
template <typename T> struct ModelT : ConceptT {
std::unique_ptr<Property> singleValueProperty(const std::string &name,
const Json::Value &value) const override final {
using ToCppT = pwvjdetail::ToCpp<T>;
return std::make_unique<PropertyWithValue<T>>(name, ToCppT()(value));
}
std::unique_ptr<Property> arrayValueProperty(const std::string &name,
const Json::Value &value) const override final {
using ToCppVectorT = pwvjdetail::ToCpp<std::vector<T>>;
return std::make_unique<ArrayProperty<T>>(name, ToCppVectorT()(value));
}
};
std::unique_ptr<ConceptT> m_self;
};
// Define a lookup mapping Json::ValueTypes to a FromJson able to
// create a PropertyWithValue object from that type
using FromJsonConverters = std::map<Json::ValueType, FromJson>;
// Returns (and creates on first call) the map of Json::ValueType to
// FromJson for converting a JsonValue to a Property object
const FromJsonConverters &converters() {
static FromJsonConverters converters;
if (converters.empty()) {
// Build a map of Json types to FromJson converters of the appropriate type
converters.insert(std::make_pair(Json::booleanValue, FromJson(&Json::Value::asBool)));
converters.insert(std::make_pair(Json::intValue, FromJson(&Json::Value::asInt)));
converters.insert(std::make_pair(Json::realValue, FromJson(&Json::Value::asDouble)));
converters.insert(std::make_pair(Json::stringValue, FromJson(&Json::Value::asString)));
}
return converters;
}
/**
* @brief Create a PropertyWithValue object from the given Json::Value
* @param name The name of the new property
* @param value The value as Json::Value. For an array is guaranteed to have
at
* least 1 element
* @param createArray If true creates an ArrayProperty
* @return A pointer to a new Property object
* @throws std::invalid_argument if the type of the Json::Value is not known
*/
std::unique_ptr<Property> createSingleTypeProperty(const std::string &name, const Json::Value &value) {
const auto isArray = value.isArray();
FromJsonConverters::const_iterator conversionFnIter;
// For an array use the first element as the type checker and the rest must
// be convertible
if (isArray)
conversionFnIter = converters().find(value[0].type());
else
conversionFnIter = converters().find(value.type());
if (conversionFnIter == converters().end()) {
throw std::invalid_argument("Cannot create property with name " + name +
". Unable to find converter "
"for Json::ValueType to C++ "
"type");
}
return conversionFnIter->second.createProperty(name, value, value.isArray());
}
/**
* @brief Create a PropertyManagerProperty from the given Json:objectValue
* @param name The name of the object
* @param keyValues A Json::objectValue containing key-value pairs
* @return A new Property object
*/
std::unique_ptr<Property> createKeyValueProperty(const std::string &name, const Json::Value &keyValues) {
return std::make_unique<PropertyManagerProperty>(name, createPropertyManager(keyValues));
}
} // namespace
/**
* @brief Create a PropertyManager from a Json Object.
* @param keyValues A Json objectValue. This is not checked.
* @return A new PropertyManager
* @throws std::invalid_argument if the Json::Value can't be interpreted
*/
PropertyManager_sptr createPropertyManager(const Json::Value &keyValues) {
auto propMgr = std::make_shared<PropertyManager>();
auto members = keyValues.getMemberNames();
for (const auto &key : members) {
const auto &value = keyValues[key];
if (value.isObject())
propMgr->declareProperty(createKeyValueProperty(key, value));
else
propMgr->declareProperty(createSingleTypeProperty(key, value));
}
return propMgr;
}
/**
* @param name The name of the new property
* @param value A value as a Json serialized quantity
* @return A pointer to a new Property if the underlying value can
* be converted to a known C++ type
* @throws std::invalid_argument If the value cannot be transformed to
* a Property object
*/
std::unique_ptr<Property> decodeAsProperty(const std::string &name, const Json::Value &value) {
if (value.isNull()) {
throw std::invalid_argument("decodeAsProperty(): Found null Json value.");
}
if (!value.isObject()) {
return createSingleTypeProperty(name, value);
} else {
return createKeyValueProperty(name, value);
}
}
} // namespace Mantid::Kernel