Newer
Older
Steve Williams
committed
#include "MantidKernel/FileValidator.h"
#include <boost/algorithm/string/case_conv.hpp>
namespace Mantid {
namespace Kernel {
namespace {
// Initialize the static logger
Logger g_log("FileValidator");
Gigg, Martyn Anthony
committed
/** Constructor
Janik Zikovsky
committed
* @param extensions :: The permitted file extensions (e.g. .RAW)
* @param testFileExists :: Flag indicating whether to test for existence of
* file (default: yes)
* @param testCanWrite :: Flag to check if file writing permissible.
FileValidator::FileValidator(const std::vector<std::string> &extensions,
bool testFileExists, bool testCanWrite)
: TypedValidator<std::string>(), m_testExist(testFileExists),
m_testCanWrite(testCanWrite) {
for (auto it = extensions.begin(); it != extensions.end(); ++it) {
const std::string ext = boost::to_lower_copy(*it);
if (std::find(m_extensions.begin(), m_extensions.end(), ext) ==
m_extensions.end()) {
m_extensions.push_back(ext);
Peterson, Peter
committed
/// Destructor
FileValidator::~FileValidator() {}
/// Returns the set of valid values
std::vector<std::string> FileValidator::allowedValues() const {
return m_extensions;
}
* Clone the validator
* @returns A pointer to a new validator with the same properties as this one
*/
IValidator_sptr FileValidator::clone() const {
return boost::make_shared<FileValidator>(*this);
Steve Williams
committed
/** If m_fullTest=true if checks that the files exists, otherwise just that path
* syntax looks valid
Janik Zikovsky
committed
* @param value :: file name
* @returns An error message to display to users or an empty string on no error
*/
std::string FileValidator::checkValidity(const std::string &value) const {
Gigg, Martyn Anthony
committed
// Check if the path is syntactically valid
if (!Poco::Path().tryParse(value)) {
Gigg, Martyn Anthony
committed
return "Error in path syntax: \"" + value + "\".";
}
// Check the extension but just issue a warning if it is not one of the
// suggested values
if (!(value.empty())) {
if (!(this->endswith(value))) {
// Dropped from warning to debug level as it was printing out on every
// search of the archive, even when successful. re #5998
g_log.debug() << "Unrecognised extension in file \"" << value << "\"";
Peterson, Peter
committed
if (!this->m_extensions.empty()) {
for (auto it = this->m_extensions.begin();
it != this->m_extensions.end(); ++it)
g_log.debug() << *it << " ";
Peterson, Peter
committed
}
g_log.debug() << "\"." << std::endl;
Peterson, Peter
committed
}
Gigg, Martyn Anthony
committed
// create a variable for the absolute path to be used in error messages
std::string abspath(value);
Poco::Path path(value);
if (path.isAbsolute())
abspath = path.toString();
}
// If the file is required to exist check it is there
if (m_testExist && (value.empty() || !Poco::File(value).exists())) {
return "File \"" + abspath + "\" not found";
Gigg, Martyn Anthony
committed
}
// If the file is required to be writable...
if (m_testCanWrite) {
if (value.empty())
return "Cannot write to empty filename";
Poco::File file(value);
// the check for writable is different for whether or not a version exists
// this is taken from ConfigService near line 443
if (!file.canWrite())
return "File \"" + abspath + "\" cannot be written";
} catch (std::exception &e) {
g_log.information()
<< "Encountered exception while checking for writable: "
<< e.what();
} else // if the file doesn't exist try to temporarily create one
// see if file is writable
if (Poco::File(direc).canWrite())
return "";
else
return "Cannot write to file \"" + direc.toString() + "\"";
g_log.debug() << "Do not have enough information to validate \""
<< abspath << "\"\n";
} catch (std::exception &e) {
g_log.information()
<< "Encountered exception while checking for writable: "
<< e.what();
} catch (...) {
g_log.information() << "Unknown exception while checking for writable";
}
}
// Otherwise we are okay, file extensions are just a suggestion so no
// validation on them is necessary
Gigg, Martyn Anthony
committed
return "";
}
Peterson, Peter
committed
/**
* Confirm that the value string ends with then ending string.
Janik Zikovsky
committed
* @param value :: The string to check the ending for.
* @param ending :: The ending the string should have.
Peterson, Peter
committed
*/
bool has_ending(const std::string &value, const std::string &ending) {
Peterson, Peter
committed
if (ending.empty()) // always match against an empty extension
return true;
if (value.length() < ending.length()) // filename is not long enough
return false;
int result =
value.compare(value.length() - ending.length(), ending.length(), ending);
Peterson, Peter
committed
return (result == 0); // only care if it matches
}
/**
* Checks the extension of a filename
Janik Zikovsky
committed
* @param value :: the filename to check
* @return flag that true if the extension matches in the filename
*/
bool FileValidator::endswith(const std::string &value) const {
Peterson, Peter
committed
if (m_extensions.empty()) // automatically match a lack of extensions
return true;
Peterson, Peter
committed
if ((m_extensions.size() == 1) && (m_extensions.begin()->empty()))
return true;
Peterson, Peter
committed
// create a lowercase copy of the filename
std::string value_copy(value);
std::transform(value_copy.begin(), value_copy.end(), value_copy.begin(),
tolower);
Peterson, Peter
committed
// check for the ending
for (auto it = m_extensions.begin(); it != m_extensions.end(); ++it) {
Peterson, Peter
committed
if (has_ending(value, *it)) // original case
return true;
if (has_ending(value_copy, *it)) // lower case
Peterson, Peter
committed
return true;
}
return false;
}
Steve Williams
committed
} // namespace Kernel
} // namespace Mantid