Skip to content
Snippets Groups Projects
FileProperty.cpp 13 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/Path.h>
#include <Poco/File.h>
namespace Mantid {
namespace API {
using namespace Mantid::Kernel;

//-----------------------------------------------------------------
// Public member functions
//-----------------------------------------------------------------
/**
 * Constructor
 * @param name ::          The name of the property
 * @param default_value :: A default value for the property
 * @param exts ::          The allowed extensions, the front entry in the vector
 * will be the default 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::vector<std::string> &exts,
                           unsigned int direction)
    : PropertyWithValue<std::string>(
          name, default_value,
          /* Create either a FileValidator or a
             DirectoryValidator, depending on Action
             */
          (action == FileProperty::Directory ||
           action == FileProperty::OptionalDirectory)
              ? boost::make_shared<DirectoryValidator>(action ==
                                                       FileProperty::Directory)
              : boost::make_shared<FileValidator>(
                    exts, (action == FileProperty::Load),
                    (action == FileProperty::Save)),
          direction),
      m_action(action), m_defaultExt(""), m_runFileProp(false),
      m_oldLoadPropValue(""), m_oldLoadFoundFile("") {
  setUp((exts.size() > 0) ? exts.front() : "");
 * @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)
    : PropertyWithValue<std::string>(
          name, default_value,
          /* Create either a FileValidator or a
             DirectoryValidator, depending on Action
             */
          (action == FileProperty::Directory ||
           action == FileProperty::OptionalDirectory)
              ? boost::make_shared<DirectoryValidator>(action ==
                                                       FileProperty::Directory)
              : boost::make_shared<FileValidator>(
                    std::vector<std::string>(1, ext),
                    (action == FileProperty::Load),
                    (action == FileProperty::Save)),
          direction),
      m_action(action), m_defaultExt(ext), m_runFileProp(false),
      m_oldLoadPropValue(""), m_oldLoadFoundFile("") {
/**
 * 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()) {
void FileProperty::setUp(const std::string &defExt) {
  if (isLoadProperty() && extsMatchRunFiles()) {
/**
 * 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();
    std::vector<std::string>::const_iterator facilityExtsBegin =
        facilityExts.begin();
    std::vector<std::string>::const_iterator facilityExtsEnd =
        facilityExts.end();
    const std::vector<std::string> allowedExts = this->allowedValues();

    for (auto it = allowedExts.begin(); it != allowedExts.end(); ++it) {
      if (std::find(facilityExtsBegin, facilityExtsEnd, *it) !=
          facilityExtsEnd) {
  } catch (Mantid::Kernel::Exception::NotFoundError &) {
  } // facility could not be found, do nothing this will return the default
    // match of false
namespace { // anonymous namespace keeps it here
void addExtension(const std::string &extension,
                  std::vector<std::string> &extensions) {
  if (std::find(extensions.begin(), extensions.end(), extension) !=
      extensions.end())
/**
 * 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 it = allowedExts.begin(); it != allowedExts.end(); ++it) {
        std::string lower(*it);
        std::string upper(*it);
        std::transform(it->begin(), it->end(), lower.begin(), tolower);
        std::transform(it->begin(), it->end(), upper.begin(), toupper);
        addExtension(*it, 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();