Newer
Older
// 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
// 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 <map>
using Json::Value;
namespace Mantid {
namespace 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 = boost::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));
}
* @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 Kernel
} // namespace Mantid