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),
m_oldLoadPropValue(""),
m_oldLoadFoundFile("")
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),
m_oldLoadPropValue(""),
m_oldLoadFoundFile("")
{
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 || 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
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("");
return isEmptyValueValid();
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/**
* 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();
}
else
{
return PropertyWithValue<std::string>::isValid();
}
}
/**
* @returns a string depending on whether an empty value is valid
*/
std::string FileProperty::isEmptyValueValid() const
{
if( isOptional() )
{
return "";
}
else
{
return "No file specified.";
}
}
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)
{
// determine the initial version of foundFile
Gigg, Martyn Anthony
committed
std::string foundFile("");
if ((propValue == m_oldLoadPropValue) && (!m_oldLoadFoundFile.empty()))
Gigg, Martyn Anthony
committed
{
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::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);
}
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
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
}