From 9877c796f3da8e7e887d556a57493854aefda577 Mon Sep 17 00:00:00 2001 From: Simon Heybrock <simon.heybrock@esss.se> Date: Wed, 19 Oct 2016 16:35:34 +0200 Subject: [PATCH] Re #17859. Moved WorkspaceProperty implementation out of header. --- Framework/API/CMakeLists.txt | 1 + .../API/inc/MantidAPI/WorkspaceProperty.h | 457 ++--------------- .../API/inc/MantidAPI/WorkspaceProperty.tcc | 462 ++++++++++++++++++ Framework/API/src/WorkspaceProperty.cpp | 4 +- Framework/DataObjects/CMakeLists.txt | 1 + .../DataObjects/src/WorkspaceProperty.cpp | 20 + 6 files changed, 534 insertions(+), 411 deletions(-) create mode 100644 Framework/API/inc/MantidAPI/WorkspaceProperty.tcc create mode 100644 Framework/DataObjects/src/WorkspaceProperty.cpp diff --git a/Framework/API/CMakeLists.txt b/Framework/API/CMakeLists.txt index df47eec2911..42d4b053a24 100644 --- a/Framework/API/CMakeLists.txt +++ b/Framework/API/CMakeLists.txt @@ -320,6 +320,7 @@ set ( INC_FILES inc/MantidAPI/WorkspaceHistory.h inc/MantidAPI/WorkspaceOpOverloads.h inc/MantidAPI/WorkspaceProperty.h + inc/MantidAPI/WorkspaceProperty.tcc inc/MantidAPI/WorkspaceUnitValidator.h inc/MantidAPI/Workspace_fwd.h ) diff --git a/Framework/API/inc/MantidAPI/WorkspaceProperty.h b/Framework/API/inc/MantidAPI/WorkspaceProperty.h index 125b1f1d66c..f059841a392 100644 --- a/Framework/API/inc/MantidAPI/WorkspaceProperty.h +++ b/Framework/API/inc/MantidAPI/WorkspaceProperty.h @@ -1,9 +1,6 @@ #ifndef MANTID_API_WORKSPACEPROPERTY_H_ #define MANTID_API_WORKSPACEPROPERTY_H_ -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- #include "MantidKernel/PropertyWithValue.h" #include "MantidAPI/IWorkspaceProperty.h" #include "MantidAPI/AnalysisDataService.h" @@ -77,430 +74,70 @@ class WorkspaceProperty : public Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>, public IWorkspaceProperty { public: - /** 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) - */ explicit WorkspaceProperty( const std::string &name, const std::string &wsName, const unsigned int direction, Kernel::IValidator_sptr validator = - Kernel::IValidator_sptr(new Kernel::NullValidator)) - : 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) - */ + Kernel::IValidator_sptr(new Kernel::NullValidator)); + explicit WorkspaceProperty( const std::string &name, const std::string &wsName, const unsigned int direction, const PropertyMode::Type optional, Kernel::IValidator_sptr validator = - Kernel::IValidator_sptr(new Kernel::NullValidator)) - : 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) - */ + Kernel::IValidator_sptr(new Kernel::NullValidator)); + explicit 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::IValidator_sptr(new Kernel::NullValidator)) - : 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 - 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) - WorkspaceProperty &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 - */ + Kernel::IValidator_sptr(new Kernel::NullValidator)); + + WorkspaceProperty(const WorkspaceProperty &right); + + WorkspaceProperty &operator=(const WorkspaceProperty &right); + boost::shared_ptr<TYPE> & - operator=(const boost::shared_ptr<TYPE> &value) override { - 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 - WorkspaceProperty &operator+=(Kernel::Property const *) override { - throw Kernel::Exception::NotImplementedError( - "+= operator is not implemented for WorkspaceProperty."); - return *this; - } - - /// 'Virtual copy constructor' - WorkspaceProperty<TYPE> *clone() const override { - return new WorkspaceProperty<TYPE>(*this); - } - - /** Get the name of the workspace - * @return The workspace's name - */ - std::string value() const override { return m_workspaceName; } - - /** Get the value the property was initialised with -its default value - * @return The default value - */ - std::string getDefault() const override { 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 - */ - std::string setValue(const std::string &value) override { - 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 - */ + operator=(const boost::shared_ptr<TYPE> &value) override; + + WorkspaceProperty &operator+=(Kernel::Property const *) override; + + WorkspaceProperty<TYPE> *clone() const override; + + std::string value() const override; + + std::string getDefault() const override; + + std::string setValue(const std::string &value) override; + std::string - setDataItem(const boost::shared_ptr<Kernel::DataItem> value) override { - 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. - */ - std::string isValid() const override { - // 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 - */ - bool isDefault() const override { return m_initialWSName == m_workspaceName; } - - /** Is the workspace property optional - * @return true if the workspace can be blank */ - bool isOptional() const override { - 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 */ - bool isLocking() const override { 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 - */ - std::vector<std::string> allowedValues() const override { - 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 - const Kernel::PropertyHistory createHistory() const override { - 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 - */ - bool store() override { - 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; - } - - Workspace_sptr getWorkspace() const override { return this->operator()(); } + setDataItem(const boost::shared_ptr<Kernel::DataItem> value) override; + + std::string isValid() const override; + + bool isDefault() const override; + + bool isOptional() const override; + bool isLocking() const override; + + std::vector<std::string> allowedValues() const override; + + const Kernel::PropertyHistory createHistory() const override; + + bool store() override; + + Workspace_sptr getWorkspace() const override; private: - /** 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. - */ - std::string 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. - */ - std::string 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. - */ - std::string 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 - void clear() override { - 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. - */ - void 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(); - } - } + std::string isValidGroup(boost::shared_ptr<WorkspaceGroup> wsGroup) const; + + std::string isValidOutputWs() const; + + std::string isOptionalWs() const; + + void clear() override; + + void retrieveWorkspaceFromADS(); /// The name of the workspace (as used by the AnalysisDataService) std::string m_workspaceName; diff --git a/Framework/API/inc/MantidAPI/WorkspaceProperty.tcc b/Framework/API/inc/MantidAPI/WorkspaceProperty.tcc new file mode 100644 index 00000000000..10587f3c81a --- /dev/null +++ b/Framework/API/inc/MantidAPI/WorkspaceProperty.tcc @@ -0,0 +1,462 @@ +#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 diff --git a/Framework/API/src/WorkspaceProperty.cpp b/Framework/API/src/WorkspaceProperty.cpp index 7284be35e72..508135087b7 100644 --- a/Framework/API/src/WorkspaceProperty.cpp +++ b/Framework/API/src/WorkspaceProperty.cpp @@ -1,11 +1,13 @@ #include "MantidAPI/Workspace.h" -#include "MantidAPI/WorkspaceProperty.h" #include "MantidAPI/IMDWorkspace.h" #include "MantidAPI/IEventWorkspace.h" #include "MantidAPI/IMDEventWorkspace.h" #include "MantidAPI/ITableWorkspace.h" #include "MantidAPI/ISplittersWorkspace.h" +// WorkspaceProperty implementation +#include "MantidAPI/WorkspaceProperty.tcc" + namespace Mantid { namespace API { ///@cond TEMPLATE diff --git a/Framework/DataObjects/CMakeLists.txt b/Framework/DataObjects/CMakeLists.txt index 8b50a5e5c41..86d9f626b6b 100644 --- a/Framework/DataObjects/CMakeLists.txt +++ b/Framework/DataObjects/CMakeLists.txt @@ -44,6 +44,7 @@ set ( SRC_FILES src/TableWorkspace.cpp src/VectorColumn.cpp src/Workspace2D.cpp + src/WorkspaceProperty.cpp src/WorkspaceSingleValue.cpp ) diff --git a/Framework/DataObjects/src/WorkspaceProperty.cpp b/Framework/DataObjects/src/WorkspaceProperty.cpp new file mode 100644 index 00000000000..fd962c00202 --- /dev/null +++ b/Framework/DataObjects/src/WorkspaceProperty.cpp @@ -0,0 +1,20 @@ +#include "MantidDataObjects/EventWorkspace.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidDataObjects/WorkspaceSingleValue.h" + +// WorkspaceProperty implementation +#include "MantidAPI/WorkspaceProperty.tcc" + +namespace Mantid { +// Note that this file is part of DataObjects, but we are extending API +namespace API { +///@cond TEMPLATE +template class MANTID_API_DLL + Mantid::API::WorkspaceProperty<Mantid::DataObjects::EventWorkspace>; +template class MANTID_API_DLL + Mantid::API::WorkspaceProperty<Mantid::DataObjects::Workspace2D>; +template class MANTID_API_DLL + Mantid::API::WorkspaceProperty<Mantid::DataObjects::WorkspaceSingleValue>; +///@endcond TEMPLATE +} // namespace API +} // namespace Mantid -- GitLab