-
Simon Heybrock authoredSimon Heybrock authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
WorkspaceProperty.tcc 17.24 KiB
#include "MantidAPI/Workspace.h"
#include "MantidAPI/WorkspaceProperty.h"
namespace Mantid {
namespace API {
/** Constructor.
* Sets the property and workspace names but initializes the workspace pointer
* to null.
* @param name :: The name to assign to the property
* @param wsName :: The name of the workspace
* @param direction :: Whether this is a Direction::Input, Direction::Output
* or Direction::InOut (Input & Output) workspace
* @param validator :: The (optional) validator to use for this property
* @throw std::out_of_range if the direction argument is not a member of the
* Direction enum (i.e. 0-2)
*/
template <typename TYPE>
WorkspaceProperty<TYPE>::WorkspaceProperty(const std::string &name,
const std::string &wsName,
const unsigned int direction,
Kernel::IValidator_sptr validator)
: Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>(
name, boost::shared_ptr<TYPE>(), validator, direction),
m_workspaceName(wsName), m_initialWSName(wsName),
m_optional(PropertyMode::Mandatory), m_locking(LockMode::Lock) {}
/** Constructor.
* Sets the property and workspace names but initialises the workspace pointer
* to null.
* @param name :: The name to assign to the property
* @param wsName :: The name of the workspace
* @param direction :: Whether this is a Direction::Input, Direction::Output
* or Direction::InOut (Input & Output) workspace
* @param optional :: If true then the property is optional
* @param validator :: The (optional) validator to use for this property
* @throw std::out_of_range if the direction argument is not a member of the
* Direction enum (i.e. 0-2)
*/
template <typename TYPE>
WorkspaceProperty<TYPE>::WorkspaceProperty(const std::string &name,
const std::string &wsName,
const unsigned int direction,
const PropertyMode::Type optional,
Kernel::IValidator_sptr validator)
: Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>(
name, boost::shared_ptr<TYPE>(), validator, direction),
m_workspaceName(wsName), m_initialWSName(wsName), m_optional(optional),
m_locking(LockMode::Lock) {}
/** Constructor.
* Sets the property and workspace names but initialises the workspace pointer
* to null.
* @param name :: The name to assign to the property
* @param wsName :: The name of the workspace
* @param direction :: Whether this is a Direction::Input, Direction::Output
* or Direction::InOut (Input & Output) workspace
* @param optional :: A boolean indicating whether the property is mandatory
* or not. Only matters
* for input properties
* @param locking :: A boolean indicating whether the workspace should read or
* write-locked when an algorithm begins. Default=true.
* @param validator :: The (optional) validator to use for this property
* @throw std::out_of_range if the direction argument is not a member of the
* Direction enum (i.e. 0-2)
*/
template <typename TYPE>
WorkspaceProperty<TYPE>::WorkspaceProperty(const std::string &name,
const std::string &wsName,
const unsigned int direction,
const PropertyMode::Type optional,
const LockMode::Type locking,
Kernel::IValidator_sptr validator)
: Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>(
name, boost::shared_ptr<TYPE>(), validator, direction),
m_workspaceName(wsName), m_initialWSName(wsName), m_optional(optional),
m_locking(locking) {}
/// Copy constructor, the default name stored in the new object is the same as
/// the default name from the original object
template <typename TYPE>
WorkspaceProperty<TYPE>::WorkspaceProperty(const WorkspaceProperty &right)
: Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>(right),
m_workspaceName(right.m_workspaceName),
m_initialWSName(right.m_initialWSName), m_optional(right.m_optional),
m_locking(right.m_locking) {}
/// Copy assignment operator. Only copies the value (i.e. the pointer to the
/// workspace)
template <typename TYPE>
WorkspaceProperty<TYPE> &WorkspaceProperty<TYPE>::
operator=(const WorkspaceProperty &right) {
if (&right == this)
return *this;
Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::operator=(right);
return *this;
}
/** Bring in the PropertyWithValue assignment operator explicitly (avoids
* VSC++ warning)
* @param value :: The value to set to
* @return assigned PropertyWithValue
*/
template <typename TYPE>
boost::shared_ptr<TYPE> &WorkspaceProperty<TYPE>::
operator=(const boost::shared_ptr<TYPE> &value) {
std::string wsName = value->name();
if (this->direction() == Kernel::Direction::Input && !wsName.empty()) {
m_workspaceName = wsName;
}
return Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::operator=(value);
}
//--------------------------------------------------------------------------------------
/// Add the value of another property
template <typename TYPE>
WorkspaceProperty<TYPE> &WorkspaceProperty<TYPE>::
operator+=(Kernel::Property const *) {
throw Kernel::Exception::NotImplementedError(
"+= operator is not implemented for WorkspaceProperty.");
return *this;
}
/// 'Virtual copy constructor'
template <typename TYPE>
WorkspaceProperty<TYPE> *WorkspaceProperty<TYPE>::clone() const {
return new WorkspaceProperty<TYPE>(*this);
}
/** Get the name of the workspace
* @return The workspace's name
*/
template <typename TYPE> std::string WorkspaceProperty<TYPE>::value() const {
return m_workspaceName;
}
/** Get the value the property was initialised with -its default value
* @return The default value
*/
template <typename TYPE>
std::string WorkspaceProperty<TYPE>::getDefault() const {
return m_initialWSName;
}
/** Set the name of the workspace.
* Also tries to retrieve it from the AnalysisDataService.
* @param value :: The new name for the workspace
* @return
*/
template <typename TYPE>
std::string WorkspaceProperty<TYPE>::setValue(const std::string &value) {
m_workspaceName = value;
if (Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::autoTrim()) {
boost::trim(m_workspaceName);
}
// Try and get the workspace from the ADS, but don't worry if we can't
try {
Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::m_value =
AnalysisDataService::Instance().retrieveWS<TYPE>(m_workspaceName);
} catch (Kernel::Exception::NotFoundError &) {
// Set to null property if not found
this->clear();
// the workspace name is not reset here, however.
}
return isValid();
}
/** 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.
* @return
*/
template <typename TYPE>
std::string WorkspaceProperty<TYPE>::setDataItem(
const boost::shared_ptr<Kernel::DataItem> value) {
boost::shared_ptr<TYPE> typed = boost::dynamic_pointer_cast<TYPE>(value);
if (typed) {
std::string wsName = typed->name();
if (this->direction() == Kernel::Direction::Input && !wsName.empty()) {
m_workspaceName = wsName;
}
Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::m_value = typed;
} else {
this->clear();
}
return isValid();
}
/** Checks whether the entered workspace is valid.
* To be valid, in addition to satisfying the conditions of any validators,
* an output property must not have an empty name and an input one must point
* to
* a workspace of the correct type.
* @returns A user level description of the problem or "" if it is valid.
*/
template <typename TYPE> std::string WorkspaceProperty<TYPE>::isValid() const {
// start with the no error condition
std::string error;
// If an output workspace it must have a name, although it might not exist
// in the ADS yet
if (this->direction() == Kernel::Direction::Output) {
return isValidOutputWs();
}
// If an input (or inout) workspace, must point to something, although it
// doesn't have to have a name
// unless it's optional
if (this->direction() == Kernel::Direction::Input ||
this->direction() == Kernel::Direction::InOut) {
// Workspace groups will not have a value since they are not of type TYPE
if (!Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::m_value) {
Mantid::API::Workspace_sptr wksp;
// if the workspace name is empty then there is no point asking the ADS
if (m_workspaceName.empty())
return isOptionalWs();
try {
wksp = AnalysisDataService::Instance().retrieve(m_workspaceName);
} catch (Kernel::Exception::NotFoundError &) {
// Check to see if the workspace is not logged with the ADS because it
// is optional.
return isOptionalWs();
}
// At this point we have a valid pointer to a Workspace so we need to
// test whether it is a group
if (boost::dynamic_pointer_cast<Mantid::API::WorkspaceGroup>(wksp)) {
return isValidGroup(
boost::dynamic_pointer_cast<Mantid::API::WorkspaceGroup>(wksp));
} else {
error = "Workspace " + this->value() + " is not of the correct type";
}
return error;
}
}
// Call superclass method to access any attached validators (which do their
// own logging)
return Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::isValid();
}
/** Indicates if the object is still pointing to the same workspace, using the
* workspace name
* @return true if the value is the same as the initial value or false
* otherwise
*/
template <typename TYPE> bool WorkspaceProperty<TYPE>::isDefault() const {
return m_initialWSName == m_workspaceName;
}
/** Is the workspace property optional
* @return true if the workspace can be blank */
template <typename TYPE> bool WorkspaceProperty<TYPE>::isOptional() const {
return (m_optional == PropertyMode::Optional);
}
/** Does the workspace need to be locked before starting an algorithm?
* @return true (default) if the workspace will be locked */
template <typename TYPE> bool WorkspaceProperty<TYPE>::isLocking() const {
return (m_locking == LockMode::Lock);
}
/** Returns the current contents of the AnalysisDataService for input
* workspaces.
* For output workspaces, an empty set is returned
* @return set of objects in AnalysisDataService
*/
template <typename TYPE>
std::vector<std::string> WorkspaceProperty<TYPE>::allowedValues() const {
if (this->direction() == Kernel::Direction::Input ||
this->direction() == Kernel::Direction::InOut) {
// If an input workspace, get the list of workspaces currently in the ADS
auto vals = AnalysisDataService::Instance().getObjectNames(
Mantid::Kernel::DataServiceSort::Sorted);
if (isOptional()) // Insert an empty option
{
vals.push_back("");
}
// Copy-construct a temporary workspace property to test the validity of
// each workspace
WorkspaceProperty<TYPE> tester(*this);
// Remove any workspace that's not valid for this algorithm
auto eraseIter = remove_if(vals.begin(), vals.end(),
[&tester](const std::string &wsName) {
return !tester.setValue(wsName).empty();
});
// Erase everything past returned iterator afterwards for readability
vals.erase(eraseIter, vals.end());
return vals;
} else {
// For output workspaces, just return an empty set
return std::vector<std::string>();
}
}
/// Create a history record
/// @return A populated PropertyHistory for this class
template <typename TYPE>
const Kernel::PropertyHistory WorkspaceProperty<TYPE>::createHistory() const {
std::string wsName = m_workspaceName;
bool isdefault = this->isDefault();
if ((wsName.empty() || this->hasTemporaryValue()) && this->operator()()) {
// give the property a temporary name in the history
std::ostringstream os;
os << "__TMP" << this->operator()().get();
wsName = os.str();
isdefault = false;
}
return Kernel::PropertyHistory(this->name(), wsName, this->type(), isdefault,
this->direction());
}
/** If this is an output workspace, store it into the AnalysisDataService
* @return True if the workspace is an output workspace and has been stored
* @throw std::runtime_error if unable to store the workspace successfully
*/
template <typename TYPE> bool WorkspaceProperty<TYPE>::store() {
bool result = false;
if (!this->operator()() && isOptional())
return result;
if (this->direction()) // Output or InOut
{
// Check that workspace exists
if (!this->operator()())
throw std::runtime_error(
"WorkspaceProperty doesn't point to a workspace");
// Note use of addOrReplace rather than add
API::AnalysisDataService::Instance().addOrReplace(m_workspaceName,
this->operator()());
result = true;
}
// always clear the internal pointer after storing
clear();
return result;
}
template <typename TYPE>
Workspace_sptr WorkspaceProperty<TYPE>::getWorkspace() const {
return this->operator()();
}
/** Checks whether the entered workspace group is valid.
* To be valid *all* members of the group have to be valid.
* @param wsGroup :: the WorkspaceGroup of which to check the validity
* @returns A user level description of the problem or "" if it is valid.
*/
template <typename TYPE>
std::string WorkspaceProperty<TYPE>::isValidGroup(
boost::shared_ptr<WorkspaceGroup> wsGroup) const {
g_log.debug() << " Input WorkspaceGroup found \n";
std::vector<std::string> wsGroupNames = wsGroup->getNames();
std::string error;
// Cycle through each workspace in the group ...
for (const auto &memberWsName : wsGroupNames) {
boost::shared_ptr<Workspace> memberWs =
AnalysisDataService::Instance().retrieve(memberWsName);
// Table Workspaces are ignored
if ("TableWorkspace" == memberWs->id()) {
error = "Workspace " + memberWsName + " is of type TableWorkspace and "
"will therefore be ignored as "
"part of the GroupedWorkspace.";
g_log.debug() << error << '\n';
} else {
// ... and if it is a workspace of incorrect type, exclude the group by
// returning an error.
if (!boost::dynamic_pointer_cast<TYPE>(memberWs)) {
error = "Workspace " + memberWsName + " is not of type " +
Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::type() +
".";
g_log.debug() << error << '\n';
return error;
}
// If it is of the correct type, it may still be invalid. Check.
else {
Mantid::API::WorkspaceProperty<TYPE> memberWsProperty(*this);
std::string memberError = memberWsProperty.setValue(memberWsName);
if (!memberError.empty())
return memberError; // Since if this member is invalid, then the
// whole group is invalid.
}
}
}
return ""; // Since all members of the group are valid.
}
/** Checks whether the entered output workspace is valid.
* To be valid the only thing it needs is a name that is allowed by the ADS,
* @see AnalysisDataServiceImpl
* @returns A user level description of the problem or "" if it is valid.
*/
template <typename TYPE>
std::string WorkspaceProperty<TYPE>::isValidOutputWs() const {
std::string error;
const std::string value = this->value();
if (!value.empty()) {
// Will the ADS accept it
error = AnalysisDataService::Instance().isValid(value);
} else {
if (isOptional())
error = ""; // Optional ones don't need a name
else
error = "Enter a name for the Output workspace";
}
return error;
}
/** Checks whether the entered workspace (that by this point we've found is
* not in the ADS)
* is actually an optional workspace and so still valid.
* @returns A user level description of the problem or "" if it is valid.
*/
template <typename TYPE>
std::string WorkspaceProperty<TYPE>::isOptionalWs() const {
std::string error;
if (m_workspaceName.empty()) {
if (isOptional()) {
error = "";
} else {
error = "Enter a name for the Input/InOut workspace";
}
} else {
error = "Workspace \"" + this->value() +
"\" was not found in the Analysis Data Service";
}
return error;
}
/// Reset the pointer to the workspace
template <typename TYPE> void WorkspaceProperty<TYPE>::clear() {
Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::m_value =
boost::shared_ptr<TYPE>();
}
/** Attempts to retreive the data from the ADS
* if the data is not foung the internal pointer is set to null.
*/
template <typename TYPE>
void WorkspaceProperty<TYPE>::retrieveWorkspaceFromADS() {
// Try and get the workspace from the ADS, but don't worry if we can't
try {
Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::m_value =
AnalysisDataService::Instance().retrieveWS<TYPE>(m_workspaceName);
} catch (Kernel::Exception::NotFoundError &) {
// Set to null property if not found
this->clear();
}
}
} // namespace API
} // namespace Mantid