Newer
Older
Gigg, Martyn Anthony
committed
//-----------------------------------------------------------------
// Includes
//-----------------------------------------------------------------
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileProperty.h"
Gigg, Martyn Anthony
committed
#include "MantidAPI/FileFinder.h"
#include "MantidKernel/FileValidator.h"
Janik Zikovsky
committed
#include "MantidKernel/DirectoryValidator.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/ConfigService.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/FacilityInfo.h"
Gigg, Martyn Anthony
committed
#include <cctype>
#include <algorithm>
#include <iostream>
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
namespace Mantid
{
namespace API
{
Gigg, Martyn Anthony
committed
using namespace Mantid::Kernel;
Janik Zikovsky
committed
Gigg, Martyn Anthony
committed
//-----------------------------------------------------------------
// Public member functions
//-----------------------------------------------------------------
/**
* Constructor
Janik Zikovsky
committed
* @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)
Gigg, Martyn Anthony
committed
*/
FileProperty::FileProperty(const std::string & name, const std::string& default_value, unsigned int action,
const std::vector<std::string> & exts, unsigned int direction)
Janik Zikovsky
committed
: 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) )
, direction),
Janik Zikovsky
committed
m_action(action),
m_defaultExt(""),
m_runFileProp(false)
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
setUp((exts.size() > 0) ? exts.front() : "");
Gigg, Martyn Anthony
committed
}
Janik Zikovsky
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
* @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)
Peterson, Peter
committed
: PropertyWithValue<std::string>(name, default_value,
Janik Zikovsky
committed
/* 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) )
Janik Zikovsky
committed
, direction),
m_action(action),
m_defaultExt(ext),
m_runFileProp(false)
{
Gigg, Martyn Anthony
committed
setUp(ext);
}
Janik Zikovsky
committed
/**
* 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
{
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;
}
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);
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
Janik Zikovsky
committed
* @return A string indicating the outcome of the attempt to set the property. An empty string indicates success.
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
std::string FileProperty::setValue(const std::string & propValue)
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
// Empty value is allowed if optional
if( propValue.empty() )
{
PropertyWithValue<std::string>::setValue("");
if( isOptional() )
{
return "";
}
else
{
return "No file specified.";
}
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
// If this looks like an absolute path then don't do any searching but make sure the
// directory exists for a Save property
if( Poco::Path(propValue).isAbsolute() )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
std::string error("");
Janik Zikovsky
committed
if( isSaveProperty() )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
error = createDirectory(propValue);
Gigg, Martyn Anthony
committed
if( !error.empty() ) return error;
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
return PropertyWithValue<std::string>::setValue(propValue);
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
std::string errorMsg("");
Gigg, Martyn Anthony
committed
// For relative paths, differentiate between load and save types
Gigg, Martyn Anthony
committed
if( isLoadProperty() )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
errorMsg = setLoadProperty(propValue);
}
else
{
errorMsg = setSaveProperty(propValue);
}
return errorMsg;
}
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
/**
* Set up the property
Janik Zikovsky
committed
* @param defExt :: The default extension
Gigg, Martyn Anthony
committed
*/
void FileProperty::setUp(const std::string & defExt)
{
m_defaultExt = defExt;
if( isLoadProperty() && extsMatchRunFiles() )
{
m_runFileProp = true;
Gigg, Martyn Anthony
committed
}
else
{
Gigg, Martyn Anthony
committed
m_runFileProp = false;
}
}
Gigg, Martyn Anthony
committed
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
* run file extensions, false otherwise
*/
bool FileProperty::extsMatchRunFiles()
{
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();
Gigg, Martyn Anthony
committed
const std::set<std::string> allowedExts = this->allowedValues();
bool match(false);
for( std::set<std::string>::const_iterator it = allowedExts.begin(); it != allowedExts.end(); ++it )
{
if( std::find(facilityExtsBegin, facilityExtsEnd, *it) != facilityExtsEnd )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
match = true;
break;
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
}
return match;
}
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())
return;
else
extensions.push_back(extension);
}
}
Gigg, Martyn Anthony
committed
/**
* 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)
{
std::string foundFile("");
if( m_runFileProp )
{
std::set<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);
addExtension(lower, exts);
std::string upper(m_defaultExt);
std::transform(m_defaultExt.begin(), m_defaultExt.end(), upper.begin(), toupper);
addExtension(upper, exts);
}
for(std::set<std::string>::iterator 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);
Gigg, Martyn Anthony
committed
}
else
{
foundFile = FileFinder::Instance().getFullPath(propValue);
}
if( foundFile.empty() )
{
return PropertyWithValue<std::string>::setValue(propValue);
}
else
{
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
{
Gigg, Martyn Anthony
committed
return PropertyWithValue<std::string>::setValue("");
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
else return "Empty filename not allowed.";
}
std::string errorMsg("");
// 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");
Poco::Path save_dir;
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() )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
save_dir = Poco::Path::current();
Gigg, Martyn Anthony
committed
}
}
Gigg, Martyn Anthony
committed
else
{
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;
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
/**
Gigg, Martyn Anthony
committed
* Create a given directory if it does not already exist.
Janik Zikovsky
committed
* @param path :: The path to the directory, which can include file stem
Gigg, Martyn Anthony
committed
* @returns A string indicating a problem if one occurred
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
std::string FileProperty::createDirectory(const std::string & path) const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
Poco::Path stempath(path);
Gigg, Martyn Anthony
committed
if( stempath.isFile() )
{
stempath.makeParent();
}
Gigg, Martyn Anthony
committed
std::string error("");
Gigg, Martyn Anthony
committed
if( !stempath.toString().empty() )
{
Poco::File stem(stempath);
if( !stem.exists() )
{
Gigg, Martyn Anthony
committed
try
{
stem.createDirectories();
}
catch(Poco::Exception &e)
{
error = e.what();
}
}
}
else
{
error = "Invalid directory.";
}
return error;
}
/**
* Check file extension to see if a lower- or upper-cased version will also match if the given one does not exist
Janik Zikovsky
committed
* @param filepath :: A filename whose extension is checked and converted to lower/upper case if necessary.
Gigg, Martyn Anthony
committed
* @returns The new filename
*/
std::string FileProperty::convertExtension(const std::string & filepath) const
{
Poco::Path fullpath(filepath);
std::string ext = fullpath.getExtension();
Gigg, Martyn Anthony
committed
if( ext.empty() ) return filepath;
Steve Williams
committed
const size_t nchars = ext.size();
for( size_t i = 0; i < nchars; ++i )
Gigg, Martyn Anthony
committed
{
int c = static_cast<int>(ext[i]);
Gigg, Martyn Anthony
committed
if( std::islower(c) )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
else if( std::isupper(c) )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
else {}
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
fullpath.setExtension(ext);
return fullpath.toString();
Gigg, Martyn Anthony
committed
}