Newer
Older
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileProperty.h"
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileFinder.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/DirectoryValidator.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/FacilityInfo.h"
#include "MantidKernel/Strings.h"
#include <Poco/Path.h>
#include <boost/make_shared.hpp>
#include <iterator>
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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);
}
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/** Expand user variables in file path.
* On Windows and UNIX, ~ is replaced by the user's home directory, if found.
* If the path contains no user variables, or expansion fails, the path is
* returned unchanged, for errors to be dealt with by the calling function.
* Note: this function does not support the "~user/blah" format for a named
* user's home directory - if this is encountered, the filepath is returned
* unchanged.
* @param filepath The path to expand
* @return The expanded path
*/
std::string expandUser(const std::string &filepath) {
auto start = filepath.begin();
if (*start != '~') // No user variables in filepath
return filepath;
// Position of the first slash after the variable
auto nextSlash = find_if(start, filepath.end(),
[](const char &c){
return c == '/' || c == '\\';});
// ~user/blah format - no support for this as yet
if (std::distance(start, nextSlash) != 1)
return filepath;
char *home;
if (!(home = std::getenv("HOME"))) // Usually set on Windows and UNIX
if (!(home = std::getenv("USERPROFILE"))) // Not usually set on UNIX
// Couldn't find any relevant environment variables
return filepath;
std::string homeStr = std::string(home);
return homeStr + std::string(nextSlash, filepath.end());
}
/**
* 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 createDirectory(const std::string &path) {
Poco::Path stempath(path);
if (stempath.isFile()) {
stempath.makeParent();
}
if (!stempath.toString().empty()) {
Poco::File stem(stempath);
if (!stem.exists()) {
try {
stem.createDirectories();
} catch (Poco::Exception &e) {
std::stringstream msg;
msg << "Failed to create directory \"" << stempath.toString()
<< "\": " << e.what();
return msg.str();
}
}
} else {
return "Invalid directory.";
}
return ""; // everything went fine
}
Gigg, Martyn Anthony
committed
//-----------------------------------------------------------------
// Public member functions
//-----------------------------------------------------------------
/**
* Constructor
* @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
* @param exts The allowed extensions. The front entry in the vector
* will be the default extension
* @param direction An optional direction (default=Input)
Gigg, Martyn Anthony
committed
*/
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("") {}
Gigg, Martyn Anthony
committed
/**
* Constructor
Janik Zikovsky
committed
* @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
Janik Zikovsky
committed
* @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, std::vector<std::string>(exts),
direction) {}
/**
* Check if this is a load property
Janik Zikovsky
committed
* @returns True if the property is a Load property and false otherwise
bool FileProperty::isLoadProperty() const {
return m_action == Load || m_action == OptionalLoad;
Janik Zikovsky
committed
/**
* Check if this is a Save property
* @returns True if the property is a Save property and false otherwise
*/
bool FileProperty::isSaveProperty() const {
Janik Zikovsky
committed
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 {
Janik Zikovsky
committed
return m_action == Directory || m_action == OptionalDirectory;
}
Gigg, Martyn Anthony
committed
/**
* 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);
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
/**
Gigg, Martyn Anthony
committed
* Set the value of the property
Janik Zikovsky
committed
* @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.
Gigg, Martyn Anthony
committed
*/
std::string FileProperty::setValue(const std::string &propValue) {
std::string strippedValue = Kernel::Strings::strip(propValue);
Gigg, Martyn Anthony
committed
// Empty value is allowed if optional
Gigg, Martyn Anthony
committed
PropertyWithValue<std::string>::setValue("");
return isEmptyValueValid();
Gigg, Martyn Anthony
committed
}
// Expand user variables, if there are any
strippedValue = expandUser(strippedValue);
// If this looks like an absolute path then don't do any searching but make
// sure the
Gigg, Martyn Anthony
committed
// directory exists for a Save property
if (Poco::Path(strippedValue).isAbsolute()) {
if (isSaveProperty()) {
error = createDirectory(strippedValue);
if (!error.empty())
return error;
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
return PropertyWithValue<std::string>::setValue(strippedValue);
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
// For relative paths, differentiate between load and save types
errorMsg = setLoadProperty(strippedValue);
errorMsg = setSaveProperty(strippedValue);
Gigg, Martyn Anthony
committed
}
return errorMsg;
}
Gigg, Martyn Anthony
committed
* 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()) {
return "No file specified.";
}
}
Gigg, Martyn Anthony
committed
/**
* 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
Gigg, Martyn Anthony
committed
* run file extensions, false otherwise
*/
bool FileProperty::extsMatchRunFiles() {
Gigg, Martyn Anthony
committed
bool match(false);
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) !=
match = true;
break;
}
Gigg, Martyn Anthony
committed
}
} catch (Mantid::Kernel::Exception::NotFoundError &) {
} // facility could not be found, do nothing this will return the default
Gigg, Martyn Anthony
committed
return match;
}
/**
* Handles the filename if this is a load property
Janik Zikovsky
committed
* @param propValue :: The filename to treat as a filepath to be loaded
Gigg, Martyn Anthony
committed
* @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;
addExtension(m_defaultExt, exts);
std::string lower(m_defaultExt);
std::transform(m_defaultExt.begin(), m_defaultExt.end(), lower.begin(),
tolower);
addExtension(lower, exts);
std::string upper(m_defaultExt);
std::transform(m_defaultExt.begin(), m_defaultExt.end(), upper.begin(),
toupper);
addExtension(upper, exts);
}
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);
Gigg, Martyn Anthony
committed
}
// cache the new version of foundFile
m_oldLoadFoundFile = foundFile;
Gigg, Martyn Anthony
committed
return PropertyWithValue<std::string>::setValue(propValue);
Gigg, Martyn Anthony
committed
return PropertyWithValue<std::string>::setValue(foundFile);
}
}
/**
* Handles the filename if this is a save property
Janik Zikovsky
committed
* @param propValue :: The filename to treat as a filepath to be saved
Gigg, Martyn Anthony
committed
* @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) {
Gigg, Martyn Anthony
committed
return PropertyWithValue<std::string>::setValue("");
} else
return "Empty filename not allowed.";
Gigg, Martyn Anthony
committed
}
// 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");
Gigg, Martyn Anthony
committed
Poco::Path save_dir;
Gigg, Martyn Anthony
committed
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()) {
Gigg, Martyn Anthony
committed
save_dir = Poco::Path::current();
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
save_dir = Poco::Path(save_path).makeDirectory();
}
errorMsg = createDirectory(save_dir.toString());
Gigg, Martyn Anthony
committed
std::string fullpath = save_dir.resolve(propValue).toString();
errorMsg = PropertyWithValue<std::string>::setValue(fullpath);
}
return errorMsg;
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
}