Skip to content
Snippets Groups Projects
FileProperty.cpp 13.1 KiB
Newer Older
//-----------------------------------------------------------------
// Includes
//-----------------------------------------------------------------
#include "MantidKernel/DirectoryValidator.h"
#include "MantidKernel/FileValidator.h"
#include "MantidKernel/Strings.h"

Campbell, Stuart's avatar
Campbell, Stuart committed
#include <Poco/File.h>
namespace Mantid {
namespace API {
using Mantid::Kernel::ConfigService;
using Mantid::Kernel::DirectoryValidator;
using Mantid::Kernel::FileValidator;
using Mantid::Kernel::IValidator_sptr;

namespace {
/**
  * Create the appropriate validator based on the parameters
  * @param action The type of property that is being defined, @see FileAction
  * @param exts A list of extensions, only use for File-type actions and are
  *             passed to the validator
  */
IValidator_sptr createValidator(unsigned int action,
                                const std::vector<std::string> &exts) {
  if (action == FileProperty::Directory ||
      action == FileProperty::OptionalDirectory) {
    return boost::make_shared<DirectoryValidator>(action ==
                                                  FileProperty::Directory);
  } else {
    return boost::make_shared<FileValidator>(
        exts, (action == FileProperty::Load), (action == FileProperty::Save));
  }
}

/**
 * If the given extension doesn't exist in the list then add it
 * @param extension A string listing the extension
 * @param extensions The existing collection
 */
void addExtension(const std::string &extension,
                  std::vector<std::string> &extensions) {
  if (std::find(extensions.begin(), extensions.end(), extension) !=
      extensions.end())
    return;
  else
    extensions.push_back(extension);
}
}

//-----------------------------------------------------------------
// Public member functions
//-----------------------------------------------------------------
/**
 * Constructor
Martyn Gigg's avatar
Martyn Gigg committed
 * @param name The name of the property
 * @param defaultValue A default value for the property
 * @param action Inndicate whether this should be a load/save
Martyn Gigg's avatar
Martyn Gigg committed
 * @param exts The allowed extensions. The front entry in the vector
 * will be the default extension
 * @param direction An optional direction (default=Input)
FileProperty::FileProperty(const std::string &name,
                           const std::string &defaultValue, unsigned int action,
                           const std::vector<std::string> &exts,
                           unsigned int direction)
    : PropertyWithValue<std::string>(name, defaultValue,
                                     createValidator(action, exts), direction),
      m_action(action), m_defaultExt((!exts.empty()) ? exts.front() : ""),
      m_runFileProp(isLoadProperty() && extsMatchRunFiles()),
      m_oldLoadPropValue(""), m_oldLoadFoundFile("") {}
 * @param name ::          The name of the property
 * @param default_value :: A default value for the property
 * @param ext ::           The allowed extension
 * @param action ::        An enum indicating whether this should be a load/save
 * property
 * @param direction ::     An optional direction (default=Input)
FileProperty::FileProperty(const std::string &name,
                           const std::string &default_value,
                           unsigned int action, const std::string &ext,
                           unsigned int direction)
    : FileProperty(name, default_value, action,
                   std::vector<std::string>(1, ext), direction) {}
/**
 * Constructor
 * @param name ::          The name of the property
 * @param default_value :: A default value for the property
 * @param exts ::          The braced-list of allowed extensions
 * @param action ::        An enum indicating whether this should be a load/save
 * property
 * @param direction ::     An optional direction (default=Input)
 */
FileProperty::FileProperty(const std::string &name,
                           const std::string &default_value,
                           unsigned int action,
                           std::initializer_list<std::string> exts,
                           unsigned int direction)
    : FileProperty(name, default_value, action,
Martyn Gigg's avatar
Martyn Gigg committed
                   std::vector<std::string>(std::move(exts)), direction) {}
/**
 * Check if this is a load property
 * @returns True if the property is a Load property and false otherwise
bool FileProperty::isLoadProperty() const {
  return m_action == Load || m_action == OptionalLoad;
/**
 * Check if this is a Save property
 * @returns True if the property is a Save property and false otherwise
 */
bool FileProperty::isSaveProperty() const {
  return m_action == Save || m_action == OptionalSave;
}

/**
 * Check if this is a directory selection property
 * @returns True if the property is a Directory property
 */
bool FileProperty::isDirectoryProperty() const {
  return m_action == Directory || m_action == OptionalDirectory;
}

/**
* Check if this property is optional
* @returns True if the property is optinal, false otherwise
*/
bool FileProperty::isOptional() const {
  return (m_action == OptionalLoad || m_action == OptionalSave ||
          m_action == OptionalDirectory);
 * @param propValue :: The value here is treated as relating to a filename
 * @return A string indicating the outcome of the attempt to set the property.
 * An empty string indicates success.
std::string FileProperty::setValue(const std::string &propValue) {
  std::string strippedValue = Kernel::Strings::strip(propValue);

  if (strippedValue.empty()) {
    PropertyWithValue<std::string>::setValue("");
    return isEmptyValueValid();
  // If this looks like an absolute path then don't do any searching but make
  // sure the
  if (Poco::Path(strippedValue).isAbsolute()) {
    if (isSaveProperty()) {
      error = createDirectory(strippedValue);
      if (!error.empty())
        return error;
    return PropertyWithValue<std::string>::setValue(strippedValue);
  // For relative paths, differentiate between load and save types
  if (isLoadProperty()) {
    errorMsg = setLoadProperty(strippedValue);
    errorMsg = setSaveProperty(strippedValue);
 * Checks whether the current value is considered valid. Use the validator
 * unless the
 * value is an empty string. In this case it is only valid if the property is
 * not optional
 * @returns an empty string if the property is valid, otherwise contains an
 * error message
std::string FileProperty::isValid() const {
  const std::string &value = (*this)();
  if (value.empty()) {
    return isEmptyValueValid();
    return PropertyWithValue<std::string>::isValid();
  }
}

/**
 * @returns a string depending on whether an empty value is valid
 */
std::string FileProperty::isEmptyValueValid() const {
  if (isOptional()) {
/**
 * Do the allowed values match the facility preference extensions for run files
 * @returns True if the extensions match those in the facility's preference list
 * for
bool FileProperty::extsMatchRunFiles() {
  try {
    Kernel::FacilityInfo facilityInfo =
        Kernel::ConfigService::Instance().getFacility();
    const std::vector<std::string> facilityExts = facilityInfo.extensions();
    auto facilityExtsBegin = facilityExts.cbegin();
    auto facilityExtsEnd = facilityExts.cend();
    const std::vector<std::string> allowedExts = this->allowedValues();

    for (const auto &ext : allowedExts) {
      if (std::find(facilityExtsBegin, facilityExtsEnd, ext) !=
          facilityExtsEnd) {
  } catch (Mantid::Kernel::Exception::NotFoundError &) {
  } // facility could not be found, do nothing this will return the default
  // match of false

  return match;
}

/**
 * Handles the filename if this is a load property
 * @param propValue :: The filename to treat as a filepath to be loaded
 * @returns A string contain the result of the operation, empty if successful.
 */
std::string FileProperty::setLoadProperty(const std::string &propValue) {
  // determine the initial version of foundFile
  if ((propValue == m_oldLoadPropValue) && (!m_oldLoadFoundFile.empty())) {
    foundFile = m_oldLoadFoundFile;
  }

  // cache the new version of propValue
  m_oldLoadPropValue = propValue;

  // if foundFile is not empty then it is the cached file
  if (foundFile.empty()) {
    if (m_runFileProp) // runfiles go through FileFinder::findRun
      std::vector<std::string> allowedExts(allowedValues());
      std::vector<std::string> exts;
      if (!m_defaultExt.empty()) {
        addExtension(m_defaultExt, exts);
        std::string lower(m_defaultExt);
        std::transform(m_defaultExt.begin(), m_defaultExt.end(), lower.begin(),
                       tolower);
        std::string upper(m_defaultExt);
        std::transform(m_defaultExt.begin(), m_defaultExt.end(), upper.begin(),
                       toupper);
      for (auto &ext : allowedExts) {
        std::string lower(ext);
        std::string upper(ext);
        std::transform(ext.begin(), ext.end(), lower.begin(), tolower);
        std::transform(ext.begin(), ext.end(), upper.begin(), toupper);
        addExtension(ext, exts);
        addExtension(lower, exts);
        addExtension(upper, exts);
      }
      foundFile = FileFinder::Instance().findRun(propValue, exts);
    } else // non-runfiles go through FileFinder::getFullPath
      foundFile = FileFinder::Instance().getFullPath(propValue);

  // cache the new version of foundFile
  m_oldLoadFoundFile = foundFile;

  if (foundFile.empty()) {
    return PropertyWithValue<std::string>::setValue(propValue);
    return PropertyWithValue<std::string>::setValue(foundFile);
  }
}

/**
 * Handles the filename if this is a save property
 * @param propValue :: The filename to treat as a filepath to be saved
 * @returns A string contain the result of the operation, empty if successful.
 */
std::string FileProperty::setSaveProperty(const std::string &propValue) {
  if (propValue.empty()) {
    if (m_action == OptionalSave) {
      return PropertyWithValue<std::string>::setValue("");
    } else
      return "Empty filename not allowed.";
  // We have a relative save path so just prepend the path that is in the
  // 'defaultsave.directory'
  // Note that this catches the Poco::NotFoundException and returns an empty
  // string in that case
  std::string save_path =
      ConfigService::Instance().getString("defaultsave.directory");
  if (save_path.empty()) {
    save_dir = Poco::Path(propValue).parent();
    // If we only have a stem filename, parent() will make save_dir empty and
    // then Poco::File throws
    if (save_dir.toString().empty()) {
    save_dir = Poco::Path(save_path).makeDirectory();
  }
  errorMsg = createDirectory(save_dir.toString());
  if (errorMsg.empty()) {
    std::string fullpath = save_dir.resolve(propValue).toString();
    errorMsg = PropertyWithValue<std::string>::setValue(fullpath);
  }
  return errorMsg;
 * Create a given directory if it does not already exist.
 * @param path :: The path to the directory, which can include file stem
 * @returns A string indicating a problem if one occurred
std::string FileProperty::createDirectory(const std::string &path) const {
  if (stempath.isFile()) {
  if (!stempath.toString().empty()) {
    if (!stem.exists()) {
      try {
      } catch (Poco::Exception &e) {
        std::stringstream msg;
        msg << "Failed to create directory \"" << stempath.toString()
            << "\": " << e.what();
    return "Invalid directory.";
  return ""; // everything went fine
 * Check file extension to see if a lower- or upper-cased version will also
 * match if the given one does not exist
 * @param filepath :: A filename whose extension is checked and converted to
 * lower/upper case if necessary.
std::string FileProperty::convertExtension(const std::string &filepath) const {
  Poco::Path fullpath(filepath);
  std::string ext = fullpath.getExtension();
  if (ext.empty())
    return filepath;
  for (size_t i = 0; i < nchars; ++i) {
    if (std::islower(c)) {
Peterson, Peter's avatar
Peterson, Peter committed
      ext[i] = static_cast<char>(std::toupper(c));
    } else if (std::isupper(c)) {
Peterson, Peter's avatar
Peterson, Peter committed
      ext[i] = static_cast<char>(std::tolower(c));
  return fullpath.toString();