Commit 1069c329 authored by Martyn Gigg's avatar Martyn Gigg
Browse files

Add Property::setValueFromJson method

Required to set existing properties from a Json object
Refs #23988
parent b4c2de2c
......@@ -274,6 +274,8 @@ public:
::Json::Value toJson() const override;
/// De-serialize an object from a string
static IAlgorithm_sptr fromString(const std::string &input);
/// De-serialize an object from a Json
static IAlgorithm_sptr fromJson(const Json::Value &input);
/// Construct an object from a history entry
static IAlgorithm_sptr fromHistory(const AlgorithmHistory &history);
//@}
......
......@@ -45,12 +45,10 @@ public:
Kernel::IValidator_sptr validator =
Kernel::IValidator_sptr(new Kernel::NullValidator),
unsigned int direction = Kernel::Direction::Input);
/// Copy constructor
AlgorithmProperty(const AlgorithmProperty &rhs);
// Unhide base class members (at minimum, avoids Intel compiler warning)
// Unhide base class member that would be hidden by implicitly declared
// assignment operator
using Kernel::PropertyWithValue<HeldType>::operator=;
/// Copy-Assignment operator
AlgorithmProperty &operator=(const AlgorithmProperty &rhs);
/// 'Virtual copy constructor'
inline AlgorithmProperty *clone() const override {
return new AlgorithmProperty(*this);
......@@ -68,15 +66,16 @@ public:
Json::Value valueAsJson() const override;
/// Get the default
std::string getDefault() const override;
/// Sets the value of the algorithm
/// Sets the value of the algorithm from a string representation
std::string setValue(const std::string &value) override;
/// Sets the value of the algorithm from a Json representation
std::string setValueFromJson(const Json::Value &value) override;
private:
/// Default constructor
AlgorithmProperty();
std::string setBaseValue(const HeldType &algm);
/// The string used to create the underlying algorithm
std::string m_algStr;
// Cached string as value() can be called frequently
std::string m_algmStr;
};
#ifdef _WIN32
......
......@@ -65,6 +65,9 @@ public:
/// Set the value of the property.
std::string setValue(const std::string &value) override;
/// Set the value of the property as a Json representation
std::string setValueFromJson(const Json::Value &value) override;
/// Checks whether the entered function is valid.
std::string isValid() const override;
......
......@@ -96,6 +96,8 @@ public:
std::string setValue(const std::string &value) override;
std::string setValueFromJson(const Json::Value &value) override;
std::string
setDataItem(const boost::shared_ptr<Kernel::DataItem> value) override;
......
......@@ -173,9 +173,10 @@ std::string WorkspaceProperty<TYPE>::getDefault() const {
}
/** Set the name of the workspace.
* Also tries to retrieve it from the AnalysisDataService.
* @param value :: The new name for the workspace
* @return
* Also tries to retrieve it from the AnalysisDataService.
* @param value :: The new name for the workspace
* @return An empty string indicating success otherwise a string containing the
* error
*/
template <typename TYPE>
std::string WorkspaceProperty<TYPE>::setValue(const std::string &value) {
......@@ -187,6 +188,23 @@ std::string WorkspaceProperty<TYPE>::setValue(const std::string &value) {
return isValid();
}
/**
* Set the name of the workspace from a Json::Value object
* Also tries to retrieve it from the AnalysisDataService.
* @param value :: The new name for the workspace
* @return An empty string indicating success otherwise a string containing the
* error
*/
template <typename TYPE>
std::string
WorkspaceProperty<TYPE>::setValueFromJson(const Json::Value &value) {
try {
return setValue(value.asString());
} catch (std::exception &exc) {
return exc.what();
}
}
/** Set a value from a data item
* @param value :: A shared pointer to a DataItem. If it is of the correct
* type it will set validated, if not the property's value will be cleared.
......
......@@ -956,38 +956,37 @@ IAlgorithm_sptr Algorithm::fromHistory(const AlgorithmHistory &history) {
/** De-serializes the algorithm from a string
*
* @param input :: An input string in the format. The format is
* AlgorithmName.version(prop1=value1,prop2=value2,...). If .version is
*not found the
* highest found is used.
* AlgorithmName.version(prop1=value1,prop2=value2,...). If .version is
* not found the highest found is used.
* @return A pointer to a managed algorithm object
* @throws std::runtime_error if the algorithm cannot be created
*/
IAlgorithm_sptr Algorithm::fromString(const std::string &input) {
::Json::Value root;
::Json::Reader reader;
if (reader.parse(input, root)) {
const std::string algName = root["name"].asString();
int version = 0;
try {
version = root["version"].asInt();
} catch (std::runtime_error &) {
// do nothing - the next test will catch it
}
if (version == 0)
version = -1;
IAlgorithm_sptr alg =
AlgorithmManager::Instance().createUnmanaged(algName, version);
alg->initialize();
// get properties
alg->setProperties(root["properties"]);
return alg;
return fromJson(root);
} else {
throw std::runtime_error("Cannot create algorithm, invalid string format.");
}
}
/**
* De-serializes the algorithm from a Json object
* @param serialized A reference to Json::Value that contains a serialized
* algorithm object
* @return A new algorithm object
* @throws std::runtime_error if the algorithm cannot be created
*/
IAlgorithm_sptr Algorithm::fromJson(const Json::Value &serialized) {
const std::string algName = serialized["name"].asString();
const int version = serialized.get("version", -1).asInt();
auto alg = AlgorithmManager::Instance().createUnmanaged(algName, version);
alg->initialize();
alg->setProperties(serialized["properties"]);
return alg;
}
//-------------------------------------------------------------------------
/** Initialize using proxy algorithm.
* Call the main initialize method and then copy in the property values.
......
......@@ -31,29 +31,13 @@ AlgorithmProperty::AlgorithmProperty(const std::string &propName,
unsigned int direction)
: Kernel::PropertyWithValue<HeldType>(propName, HeldType(), validator,
direction),
m_algStr("") {}
/**
* Copy constructor
*/
AlgorithmProperty::AlgorithmProperty(const AlgorithmProperty &rhs)
: Kernel::PropertyWithValue<HeldType>(rhs) {}
/**
* Copy-Assignment operator
*/
AlgorithmProperty &AlgorithmProperty::operator=(const AlgorithmProperty &rhs) {
if (&rhs != this) {
Kernel::PropertyWithValue<HeldType>::operator=(rhs);
}
return *this;
}
m_algmStr() {}
/**
* Return the algorithm as string
* @returns The algorithm serialized as a string
*/
std::string AlgorithmProperty::value() const { return m_algStr; }
std::string AlgorithmProperty::value() const { return m_algmStr; }
/**
* @return A Json::Value objectValue encoding the algorithm
......@@ -76,19 +60,44 @@ std::string AlgorithmProperty::getDefault() const { return ""; }
* contain the error
*/
std::string AlgorithmProperty::setValue(const std::string &value) {
try {
return setBaseValue(Algorithm::fromString(value));
} catch (std::exception &exc) {
return exc.what();
}
}
/**
* Set the value of the algorithm property from a Json value
* @param value A reference
* @return An empty string if the value is valid, otherwise the string will
* contain the error
*/
std::string AlgorithmProperty::setValueFromJson(const Json::Value &value) {
try {
return setBaseValue(Algorithm::fromJson(value));
} catch (std::exception &exc) {
return exc.what();
}
}
/**
* Set the value from the algorithm pointer
* @param algm
* @return An empty string if the value is valid, otherwise the string will
* contain the error
*/
std::string
AlgorithmProperty::setBaseValue(const AlgorithmProperty::HeldType &algm) {
std::string message;
try {
Kernel::PropertyWithValue<IAlgorithm_sptr>::m_value =
Algorithm::fromString(value);
} catch (Kernel::Exception::NotFoundError &e) {
message = e.what();
} catch (std::runtime_error &e) {
Kernel::PropertyWithValue<IAlgorithm_sptr>::m_value = algm;
} catch (std::exception &e) {
message = e.what();
}
if (message.empty()) {
m_algStr = value;
// Check against validators
m_algmStr = algm->toString();
return isValid();
} else
return message;
......
......@@ -94,6 +94,20 @@ std::string FunctionProperty::setValue(const std::string &value) {
return error;
}
/**
* Assumes the Json object is a string and parses it to create the function
* @param value A Json::Value containing a string
* @return An empty string indicating success otherwise the string will contain
* the value of the error.
*/
std::string FunctionProperty::setValueFromJson(const Json::Value &value) {
try {
return setValue(value.asString());
} catch (std::exception &exc) {
return exc.what();
}
}
/** Checks whether the entered function is valid.
* To be valid it has to be other then default which is no function defined.
* @returns A user level description of the problem or "" if it is valid.
......
......@@ -80,17 +80,19 @@ public:
static void destroySuite(AlgorithmPropertyTest *suite) { delete suite; }
AlgorithmPropertyTest() {
Mantid::API::AlgorithmFactory::Instance().subscribe<SimpleSum>();
Mantid::API::AlgorithmFactory::Instance().subscribe<HasAlgProp>();
Mantid::API::AlgorithmFactory::Instance()
.subscribe<HasAlgPropAndValidator>();
using Mantid::API::AlgorithmFactory;
auto &algmFactory = AlgorithmFactory::Instance();
algmFactory.subscribe<SimpleSum>();
algmFactory.subscribe<HasAlgProp>();
algmFactory.subscribe<HasAlgPropAndValidator>();
}
~AlgorithmPropertyTest() override {
Mantid::API::AlgorithmFactory::Instance().unsubscribe("SimpleSum", 1);
Mantid::API::AlgorithmFactory::Instance().unsubscribe("HasAlgProp1", 1);
Mantid::API::AlgorithmFactory::Instance().unsubscribe(
"HasAlgPropAndValidator1", 1);
using Mantid::API::AlgorithmFactory;
auto &algmFactory = AlgorithmFactory::Instance();
algmFactory.unsubscribe("SimpleSum", 1);
algmFactory.unsubscribe("HasAlgProp1", 1);
algmFactory.unsubscribe("HasAlgPropAndValidator1", 1);
}
void test_A_Valid_Alg_String_Is_Accepted() {
......@@ -158,6 +160,60 @@ public:
TS_ASSERT_EQUALS(adder.version(), jsonValue["version"].asInt());
TS_ASSERT_EQUALS(3, jsonValue["properties"]["Output1"].asInt());
}
void test_SetValueFromJson_With_Valid_Json() {
AlgorithmProperty prop("PropName");
const std::string helpMessage{prop.setValueFromJson(createAlgorithmJson())};
TS_ASSERT(helpMessage.empty());
auto algorithm = prop();
TS_ASSERT_EQUALS(algorithm->toString(), prop.value());
auto getIntProperty = [&algorithm](const std::string &name) {
return static_cast<int>(algorithm->getProperty(name));
};
TS_ASSERT_EQUALS(5, getIntProperty("Input1"));
TS_ASSERT_EQUALS(10, getIntProperty("Input2"));
TS_ASSERT_EQUALS(15, getIntProperty("Output1"));
}
void test_SetValueFromJson_With_Invalid_Json() {
Json::Value algm{1};
AlgorithmProperty prop("PropName");
const std::string helpMessage{prop.setValueFromJson(algm)};
TS_ASSERT(!helpMessage.empty());
}
void test_Copy_Constructor() {
AlgorithmProperty src("PropName");
src.setValueFromJson(createAlgorithmJson());
AlgorithmProperty dest{src};
TS_ASSERT_EQUALS(src.value(), dest.value());
TS_ASSERT_EQUALS(src.valueAsJson(), dest.valueAsJson());
}
void test_Move_Constructor() {
AlgorithmProperty src("PropName");
const auto algJson = createAlgorithmJson();
src.setValueFromJson(algJson);
AlgorithmProperty dest{std::move(src)};
TS_ASSERT_EQUALS(Algorithm::fromJson(algJson)->toString(), dest.value());
TS_ASSERT_EQUALS(algJson, dest.valueAsJson());
}
private:
Json::Value createAlgorithmJson() {
Json::Value algm;
algm["name"] = "SimpleSum";
algm["version"] = 1;
Json::Value properties;
properties["Input1"] = 5;
properties["Input2"] = 10;
properties["Output1"] = 15;
algm["properties"] = properties;
return algm;
}
};
#endif /* MANTID_API_ALGORITHMPROPERTYTEST_H_ */
......@@ -40,17 +40,16 @@ public:
}
static void destroySuite(FunctionPropertyTest *suite) { delete suite; }
void testConstructor() {
void test_Constructor() {
TS_ASSERT_THROWS_NOTHING(FunctionProperty prop("fun"));
}
void testValue() {
void test_Value() {
FunctionProperty prop("fun");
TS_ASSERT_EQUALS("", prop.value());
std::string error;
TS_ASSERT_THROWS_NOTHING(
error = prop.setValue("name=FunctionPropertyTest_Function,A=3"));
TS_ASSERT_THROWS_NOTHING(error = prop.setValue(createTestFunctionString()));
TS_ASSERT(error.empty());
boost::shared_ptr<IFunction> fun_p = prop;
TS_ASSERT_EQUALS(fun_p->asString(),
......@@ -61,14 +60,23 @@ public:
"name=FunctionPropertyTest_Function,A=3,B=2");
}
void testValueAsJson() {
void test_ValueAsJson() {
FunctionProperty prop("fun");
const std::string funcString("name=FunctionPropertyTest_Function,A=3,B=2");
prop.setValue(funcString);
TS_ASSERT_EQUALS(funcString, prop.valueAsJson().asString());
}
void testBadValue() {
void test_SetValueFromJson() {
FunctionProperty prop("fun");
const std::string helpMessage{
prop.setValueFromJson(Json::Value(createTestFunctionString()))};
TS_ASSERT(helpMessage.empty());
TS_ASSERT_EQUALS("name=FunctionPropertyTest_Function,A=3,B=2",
prop.value());
}
void test_Bad_Value() {
FunctionProperty prop("fun");
std::string error;
TS_ASSERT_THROWS_NOTHING(
......@@ -78,15 +86,14 @@ public:
TS_ASSERT(!error.empty());
}
void testSetValue() {
void test_Assignment_By_SharedPtr() {
FunctionProperty prop("fun");
std::string error;
boost::shared_ptr<IFunction> fun_p(
FunctionFactory::Instance().createInitialized(
"name=FunctionPropertyTest_Function,A=3"));
auto fun_p = FunctionFactory::Instance().createInitialized(
createTestFunctionString());
TS_ASSERT(fun_p);
prop = fun_p;
boost::shared_ptr<IFunction> fun1_p = prop;
auto fun1_p = prop();
TS_ASSERT(fun1_p);
TS_ASSERT_EQUALS(fun_p, fun1_p);
TS_ASSERT_EQUALS(fun1_p->asString(),
......@@ -95,7 +102,9 @@ public:
TS_ASSERT_EQUALS(fun1_p->getParameter("B"), 2.0);
}
void testSharedPointer() {
void test_SetValue_From_Json() {}
void test_Shared_Pointer() {
FunctionProperty prop("fun");
std::string error;
boost::shared_ptr<FunctionPropertyTest_Function> fun_p(
......@@ -113,6 +122,9 @@ public:
}
private:
std::string createTestFunctionString() {
return "name=FunctionPropertyTest_Function,A=3";
}
};
#endif /*FUNCTIONPROPERTYTEST_H_*/
......@@ -39,6 +39,7 @@ public:
std::string value() const override { return "Nothing"; }
Json::Value valueAsJson() const override { return Json::Value(); }
std::string setValue(const std::string &) override { return ""; }
std::string setValueFromJson(const Json::Value &) override { return ""; }
std::string setValueFromProperty(const Property &) override { return ""; }
std::string setDataItem(const boost::shared_ptr<DataItem>) override {
return "";
......
......@@ -38,6 +38,7 @@ public:
std::string value() const override { return "Nothing"; }
Json::Value valueAsJson() const override { return Json::Value(); }
std::string setValue(const std::string &) override { return ""; }
std::string setValueFromJson(const Json::Value &) override { return ""; }
std::string setValueFromProperty(const Property &) override { return ""; }
std::string setDataItem(const boost::shared_ptr<DataItem>) override {
return "";
......
......@@ -38,6 +38,14 @@ class WorkspacePropertyTest : public CxxTest::TestSuite {
const std::string id() const override { return "WorkspacePropTest"; }
};
using WorkspacePropertyWorkspace = WorkspaceProperty<Workspace>;
using WorkspacePropertyWorkspace_uptr =
std::unique_ptr<WorkspacePropertyWorkspace>;
using WorkspacePropertyWorkspaceTester2 = WorkspaceProperty<WorkspaceTester2>;
using WorkspacePropertyWorkspaceTester2_uptr =
std::unique_ptr<WorkspacePropertyWorkspaceTester2>;
public:
// This pair of boilerplate methods prevent the suite being created statically
// This means the constructor isn't called when running other tests
......@@ -48,32 +56,23 @@ public:
WorkspacePropertyTest() {
AnalysisDataService::Instance().clear();
wsp1 =
new WorkspaceProperty<Workspace>("workspace1", "ws1", Direction::Input);
wsp2 =
new WorkspaceProperty<Workspace>("workspace2", "", Direction::Output);
wsp3 = new WorkspaceProperty<WorkspaceTester2>("workspace3", "ws3",
Direction::InOut);
// Two optional properties of different types
wsp4 = new WorkspaceProperty<Workspace>("workspace4", "", Direction::Input,
PropertyMode::Optional);
wsp5 = new WorkspaceProperty<WorkspaceTester2>(
"workspace5", "", Direction::Input, PropertyMode::Optional);
wsp6 = new WorkspaceProperty<Workspace>("InvalidNameTest", "",
Direction::Output);
WorkspaceFactory::Instance().subscribe<WorkspaceTester1>(
"WorkspacePropertyTest");
WorkspaceFactory::Instance().subscribe<WorkspaceTester2>(
"WorkspacePropertyTest2");
}
~WorkspacePropertyTest() override {
delete wsp1;
delete wsp2;
delete wsp3;
delete wsp4;
delete wsp5;
delete wsp6;
wsp1 = std::make_unique<WorkspacePropertyWorkspace>("workspace1", "ws1",
Direction::Input);
wsp2 = std::make_unique<WorkspacePropertyWorkspace>("workspace2", "",
Direction::Output);
wsp3 = std::make_unique<WorkspacePropertyWorkspaceTester2>(
"workspace3", "ws3", Direction::InOut);
// Two optional properties of different types
wsp4 = std::make_unique<WorkspacePropertyWorkspace>(
"workspace4", "", Direction::Input, PropertyMode::Optional);
wsp5 = std::make_unique<WorkspacePropertyWorkspaceTester2>(
"workspace5", "", Direction::Input, PropertyMode::Optional);
wsp6 = std::make_unique<WorkspacePropertyWorkspace>("InvalidNameTest", "",
Direction::Output);
}
void testConstructor() {
......@@ -110,10 +109,17 @@ public:
TS_ASSERT_EQUALS(wsp1->setValue(""),
"Enter a name for the Input/InOut workspace")
TS_ASSERT_EQUALS(wsp1->value(), "")
TS_ASSERT_EQUALS(wsp1->setValueFromJson(Json::Value("")),
"Enter a name for the Input/InOut workspace")
TS_ASSERT_EQUALS(wsp1->value(), "")
TS_ASSERT_EQUALS(
wsp1->setValue("newValue"),
"Workspace \"newValue\" was not found in the Analysis Data Service")
TS_ASSERT_EQUALS(wsp1->value(), "newValue")
TS_ASSERT_EQUALS(
wsp1->setValueFromJson(Json::Value("newValue")),
"Workspace \"newValue\" was not found in the Analysis Data Service")
TS_ASSERT_EQUALS(wsp1->value(), "newValue")
wsp1->setValue("ws1");
}
......@@ -403,12 +409,12 @@ public:
}
private:
WorkspaceProperty<Workspace> *wsp1;
WorkspaceProperty<Workspace> *wsp2;
WorkspaceProperty<WorkspaceTester2> *wsp3;
WorkspaceProperty<Workspace> *wsp4;
WorkspaceProperty<WorkspaceTester2> *wsp5;
WorkspaceProperty<Workspace> *wsp6;
WorkspacePropertyWorkspace_uptr wsp1;
WorkspacePropertyWorkspace_uptr wsp2;
WorkspacePropertyWorkspaceTester2_uptr wsp3;
WorkspacePropertyWorkspace_uptr wsp4;
WorkspacePropertyWorkspaceTester2_uptr wsp5;
WorkspacePropertyWorkspace_uptr wsp6;
};
#endif /*WORKSPACEPROPERTYTEST_H_*/
......@@ -15,12 +15,12 @@
#ifndef Q_MOC_RUN
#include <boost/make_shared.hpp>
#include <type_traits>
#endif
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <vector>
......@@ -58,7 +58,6 @@ template <typename T> class Matrix;
*/
class MANTID_KERNEL_DLL IPropertyManager {
public:
// IPropertyManager(){}
virtual ~IPropertyManager() = default;
/// Function to declare properties (i.e. store them)
......@@ -116,6 +115,13 @@ public:
virtual void setPropertyValue(const std::string &name,
const std::string &value) = 0;
/** Sets property value from a Json::Value
@param name :: Property name
@param value :: New property value
*/
virtual void setPropertyValueFromJson(const std::string &name,
const Json::Value &value) = 0;
/// Set the value of a property by an index
virtual void setPropertyOrdinal(const int &index,
const std::string &value) = 0;
......
......@@ -143,6 +143,12 @@ public:
/// Set the value of the property via a string. If the value is unacceptable