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