diff --git a/Code/Mantid/Kernel/inc/MantidKernel/ConfigService.h b/Code/Mantid/Kernel/inc/MantidKernel/ConfigService.h index b1b92eeb7e3be3c8b1e8bfdadb8e00a86305b568..7a90af2ff33b0eaa8647889c2b86aca32c827765 100644 --- a/Code/Mantid/Kernel/inc/MantidKernel/ConfigService.h +++ b/Code/Mantid/Kernel/inc/MantidKernel/ConfigService.h @@ -19,178 +19,188 @@ /// @cond Exclude from doxygen documentation namespace Poco { - namespace Util - { - class PropertyFileConfiguration; - class SystemConfiguration; - } + namespace Util + { + class PropertyFileConfiguration; + class SystemConfiguration; + } } /// @endcond namespace Mantid { -namespace Kernel -{ -class Logger; + namespace Kernel + { + class Logger; -/** The ConfigService class provides a simple facade to access the Configuration functionality of the Mantid Framework. - The class gathers information from config files and the system variables. - This information is available to all the objects within the framework as well as being used to configure the logging framework. - This class currently uses the Logging functionality provided through the POCO (portable components library). + /** The ConfigService class provides a simple facade to access the Configuration functionality of the Mantid Framework. + The class gathers information from config files and the system variables. + This information is available to all the objects within the framework as well as being used to configure the logging framework. + This class currently uses the Logging functionality provided through the POCO (portable components library). - @author Nicholas Draper, Tessella Support Services plc - @date 15/10/2007 + @author Nicholas Draper, Tessella Support Services plc + @date 15/10/2007 - Copyright © 2007 STFC Rutherford Appleton Laboratories - - This file is part of Mantid. - - Mantid is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - Mantid is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + Copyright © 2007 STFC Rutherford Appleton Laboratories + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>. + Code Documentation is available at: <http://doxygen.mantidproject.org> + */ + class EXPORT_OPT_MANTID_KERNEL ConfigServiceImpl + { + /** Inner templated class to wrap the poco library objects that have protected + * desctructors and expose them as public. + */ + template<typename T > + class WrappedObject : public T + { + public: + /// The template type of class that is being wrapped + typedef T element_type; + /// Simple constructor + WrappedObject() : T() + { + m_pPtr = static_cast<T*>(this); + } - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. + /** Constructor with a class to wrap + * @param F The object to wrap + */ + template<typename Field> + WrappedObject(Field& F) : T(F) + { + m_pPtr = static_cast<T*>(this); + } - File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>. - Code Documentation is available at: <http://doxygen.mantidproject.org> -*/ - class EXPORT_OPT_MANTID_KERNEL ConfigServiceImpl + /// Copy constructor + WrappedObject(const WrappedObject<T>& A) : T(A) { - /** Inner templated class to wrap the poco library objects that have protected - * desctructors and expose them as public. - */ - template<typename T > - class WrappedObject : public T - { - public: - /// The template type of class that is being wrapped - typedef T element_type; - /// Simple constructor - WrappedObject() : T() - { - m_pPtr = static_cast<T*>(this); - } - - /** Constructor with a class to wrap - * @param F The object to wrap - */ - template<typename Field> - WrappedObject(Field& F) : T(F) - { - m_pPtr = static_cast<T*>(this); - } - - /// Copy constructor - WrappedObject(const WrappedObject<T>& A) : T(A) - { - m_pPtr = static_cast<T*>(this); - } + m_pPtr = static_cast<T*>(this); + } - /// Virtual destructor - virtual ~WrappedObject() - {} + /// Virtual destructor + virtual ~WrappedObject() + {} - /// Overloaded * operator returns the wrapped object pointer - const T& operator*() const { return *m_pPtr; } - /// Overloaded * operator returns the wrapped object pointer - T& operator*() { return m_pPtr; } - /// Overloaded -> operator returns the wrapped object pointer - const T* operator->() const{ return m_pPtr; } - /// Overloaded -> operator returns the wrapped object pointer - T* operator->() { return m_pPtr; } - - private: - /// Private pointer to the wrapped class - T* m_pPtr; - }; - - // Back to the ConfigService class itself... + /// Overloaded * operator returns the wrapped object pointer + const T& operator*() const { return *m_pPtr; } + /// Overloaded * operator returns the wrapped object pointer + T& operator*() { return m_pPtr; } + /// Overloaded -> operator returns the wrapped object pointer + const T* operator->() const{ return m_pPtr; } + /// Overloaded -> operator returns the wrapped object pointer + T* operator->() { return m_pPtr; } + + private: + /// Private pointer to the wrapped class + T* m_pPtr; + }; + + // Back to the ConfigService class itself... - public: - // Loads a config file - void loadConfig(const std::string& filename, const bool append=false); + public: + // Loads a config file + void loadConfig(const std::string& filename, const bool append=false); - // Searches for a configuration property - std::string getString(const std::string& keyName); - - // Searches for a configuration property and returns its value - template<typename T> - int getValue(const std::string& keyName, T& out); - - // Searches for the given environment variable and returns it as a string - std::string getEnvironment(const std::string& keyName); - - // Getters for properties of the host system - std::string getOSName(); - std::string getComputerName(); - std::string getOSArchitecture(); - std::string getOSVersion(); - std::string getCurrentDir(); - std::string getTempDir(); - std::string getBaseDir() const; - std::string getOutputDir() const; - - private: - friend struct Mantid::Kernel::CreateUsingNew<ConfigServiceImpl>; + // Searches for a configuration property + std::string getString(const std::string& keyName); + + // Searches for a configuration property and returns its value + template<typename T> + int getValue(const std::string& keyName, T& out); + + // Searches for the given environment variable and returns it as a string + std::string getEnvironment(const std::string& keyName); + + // Getters for properties of the host system + std::string getOSName(); + std::string getComputerName(); + std::string getOSArchitecture(); + std::string getOSVersion(); + std::string getCurrentDir(); + std::string getTempDir(); + std::string getBaseDir() const; + std::string getOutputDir() const; + + /// Get the list of search paths + const std::vector<std::string>& getDataSearchDirs() const; + + private: + friend struct Mantid::Kernel::CreateUsingNew<ConfigServiceImpl>; - // Private constructors and destructor for singleton class - ConfigServiceImpl(); - /// Private copy constructor. Prevents singleton being copied. - ConfigServiceImpl(const ConfigServiceImpl&); + // Private constructors and destructor for singleton class + ConfigServiceImpl(); + /// Private copy constructor. Prevents singleton being copied. + ConfigServiceImpl(const ConfigServiceImpl&); - virtual ~ConfigServiceImpl(); + virtual ~ConfigServiceImpl(); + + /// Provies a string of a default configuration + const std::string defaultConfig() const; - /// Provies a string of a default configuration - const std::string defaultConfig() const; + /// Writes out a fresh user properties file + void createUserPropertiesFile() const; - /// Writes out a fresh user properties file - void createUserPropertiesFile() const; + /// Convert any relative paths to absolute ones and store them locally so that + /// if the working directory is altered the paths will not be affected + void convertRelativeToAbsolute(); - /// the POCO file config object - WrappedObject<Poco::Util::PropertyFileConfiguration>* m_pConf; - /// the POCO system Config Object - WrappedObject<Poco::Util::SystemConfiguration>* m_pSysConfig; + /// Create the storage of the data search directories + void defineDataSearchPaths(); - /// Convert any relative paths to absolute ones and store them locally so that - /// if the working directory is altered the paths will not be affected - void convertRelativePaths(); + private: + /// the POCO file config object + WrappedObject<Poco::Util::PropertyFileConfiguration>* m_pConf; + /// the POCO system Config Object + WrappedObject<Poco::Util::SystemConfiguration>* m_pSysConfig; - /// static reference to the logger class - Logger& g_log; + /// static reference to the logger class + Logger& g_log; - /// A list of keys that may contain relative paths and need to be altered - std::vector<std::string> m_vConfigPaths; + /// A list of keys that may contain relative paths and need to be altered + std::vector<std::string> m_vConfigPaths; - /// Local storage for the relative path key/values that have been changed - std::map<std::string, std::string> m_mAbsolutePaths; + /// Local storage for the relative path key/values that have been changed + std::map<std::string, std::string> m_mAbsolutePaths; - /// The directory that is considered to be the base directory - std::string m_strBaseDir; + /// The directory that is considered to be the base directory + std::string m_strBaseDir; - ///The configuration properties in string format - std::string m_PropertyString; + ///The configuration properties in string format + std::string m_PropertyString; - /// The filename of the Mantid properties file - const std::string m_properties_file_name; - /// The filename of the Mantid user properties file - const std::string m_user_properties_file_name; - }; + /// The filename of the Mantid properties file + const std::string m_properties_file_name; + /// The filename of the Mantid user properties file + const std::string m_user_properties_file_name; + + /// Store a list of data search paths + std::vector<std::string> m_vDataSearchDirs; + }; -///Forward declaration of a specialisation of SingletonHolder for AlgorithmFactoryImpl (needed for dllexport/dllimport) and a typedef for it. + ///Forward declaration of a specialisation of SingletonHolder for AlgorithmFactoryImpl (needed for dllexport/dllimport) and a typedef for it. #ifdef __APPLE__ -inline + inline #endif -template class EXPORT_OPT_MANTID_KERNEL Mantid::Kernel::SingletonHolder<ConfigServiceImpl>; -typedef EXPORT_OPT_MANTID_KERNEL Mantid::Kernel::SingletonHolder<ConfigServiceImpl> ConfigService; + template class EXPORT_OPT_MANTID_KERNEL Mantid::Kernel::SingletonHolder<ConfigServiceImpl>; + typedef EXPORT_OPT_MANTID_KERNEL Mantid::Kernel::SingletonHolder<ConfigServiceImpl> ConfigService; -} // namespace Kernel + } // namespace Kernel } // namespace Mantid #endif /*MANTID_KERNEL_CONFIGSERVICE_H_*/ diff --git a/Code/Mantid/Kernel/inc/MantidKernel/FileProperty.h b/Code/Mantid/Kernel/inc/MantidKernel/FileProperty.h new file mode 100644 index 0000000000000000000000000000000000000000..66b9d5c9aaa04eb2d4a23cf73b6939e6cb208083 --- /dev/null +++ b/Code/Mantid/Kernel/inc/MantidKernel/FileProperty.h @@ -0,0 +1,51 @@ +#ifndef MANTID_KERNEL_FILEPROPERTY_H_ +#define MANTID_KERNEL_FILEPROPERTY_H_ + +//----------------------------------------------------------------- +// Includes +//----------------------------------------------------------------- +#include "MantidKernel/PropertyWithValue.h" +#include "MantidKernel/FileValidator.h" + +namespace Mantid +{ +namespace Kernel +{ + /** + A specialized class for dealing with file properties. Mantid allows multiple + search paths to be defined so that each of these is used when attempting to + load a file with a relative path. + + When attempting to load a file this class handles searching the specified paths and, + if found, the <code>value()</code> a method returns the full path to the file. For + saving, Mantid's default save directory is used when a relative path is encountered. + */ +class FileProperty : public PropertyWithValue<std::string> +{ +public: + /// An enumeration for load/save types. This is passed on to the FileValidator as a constructor parameter. + enum + { + // Note that changing this order will break things! + Save = 0, + Load = 1 + }; + +public: + ///Constructor + FileProperty(const std::string & name, const std::string& default_value, unsigned int action, + const std::vector<std::string> & exts = std::vector<std::string>(), + unsigned int direction = Direction::Input); + + ///Overridden setValue method + virtual std::string setValue(const std::string & filename); + +private: + /// The action type of this property, i.e. load/save + unsigned int m_action; +}; + +} +} + +#endif //MANTID_KERNEL_FILEPROPERTY_H_ diff --git a/Code/Mantid/Kernel/src/ConfigService.cpp b/Code/Mantid/Kernel/src/ConfigService.cpp index 4d1eb4615f6ea6d36e72b66f7887ee1cb9d09b9c..b2d5bc6f6bed9665fb4181bb31b486401993430b 100644 --- a/Code/Mantid/Kernel/src/ConfigService.cpp +++ b/Code/Mantid/Kernel/src/ConfigService.cpp @@ -13,6 +13,8 @@ #include "Poco/LoggingFactory.h" #include "Poco/Path.h" #include "Poco/File.h" +#include "Poco/StringTokenizer.h" + #include <fstream> #include <sstream> #include <iostream> @@ -20,417 +22,461 @@ namespace Mantid { - namespace Kernel - { - //------------------------------- - // Private member functions - //------------------------------- - - /// Private constructor for singleton class - ConfigServiceImpl::ConfigServiceImpl() : - g_log(Logger::get("ConfigService")), - m_properties_file_name("Mantid.properties"), - m_user_properties_file_name("Mantid.user.properties") - { - //getting at system details - m_pSysConfig = new WrappedObject<Poco::Util::SystemConfiguration>; - m_pConf = 0; - - //Register the FilterChannel with the Poco logging factory - Poco::LoggingFactory::defaultFactory().registerChannelClass("FilterChannel",new Poco::Instantiator<Poco::FilterChannel, Poco::Channel>); - - //Register the SignalChannel with the Poco logging factory - Poco::LoggingFactory::defaultFactory().registerChannelClass("SignalChannel",new Poco::Instantiator<Poco::SignalChannel, Poco::Channel>); - - // this Mantid API might be being called from python, MantidPlot or Matlab, we are going to check - std::string callingApplication = getPathToExecutable(); - callingApplication = Poco::Path(callingApplication).getFileName(); + namespace Kernel + { + //------------------------------- + // Private member functions + //------------------------------- + + /// Private constructor for singleton class + ConfigServiceImpl::ConfigServiceImpl() : + m_pConf(NULL), m_pSysConfig(NULL), + g_log(Logger::get("ConfigService")), + m_vConfigPaths(), m_mAbsolutePaths(), + m_strBaseDir(""), m_PropertyString(""), + m_properties_file_name("Mantid.properties"), + m_user_properties_file_name("Mantid.user.properties"), + m_vDataSearchDirs() + { + //getting at system details + m_pSysConfig = new WrappedObject<Poco::Util::SystemConfiguration>; + m_pConf = 0; + + //Register the FilterChannel with the Poco logging factory + Poco::LoggingFactory::defaultFactory().registerChannelClass("FilterChannel",new Poco::Instantiator<Poco::FilterChannel, Poco::Channel>); + + //Register the SignalChannel with the Poco logging factory + Poco::LoggingFactory::defaultFactory().registerChannelClass("SignalChannel",new Poco::Instantiator<Poco::SignalChannel, Poco::Channel>); + + // Define a directory that serves as the 'application directory'. This is where we expect to find the Mantid.properties file + // It cannot simply be the current directory since the application may be called from a different place, .i.e. + // on Linux where the bin directory is in the path and the app is run from a different location. + // We have two scenarios: + // 1) The framework is compiled into an executable and its directory is then considered as the base or, + // 2) The framework is in a stand-alone library and is created from within another executing application + // e.g. Python or Matlab (only two at the moment). We can only use the current directory here as there + // is no programmatic way of determing where the library that contains this class is. + + // A MANTID environmental variable might solve all of this?? + + std::string callingApplication = Poco::Path(getPathToExecutable()).getFileName(); // the cases used in the names varies on different systems so we do this case insensitive std::transform(callingApplication.begin(), callingApplication.end(), - callingApplication.begin(), tolower); - // WONT WORK if they rename the python executable - if ( callingApplication.find("python") != std::string::npos ) + callingApplication.begin(), tolower); + if ( callingApplication.find("python") != std::string::npos || callingApplication.find("matlab") != std::string::npos) { - // do not look for property files in the system's python directory - m_strBaseDir = Poco::Path::current(); - } - else if ( callingApplication.find("matlab") != std::string::npos ) - { - // look for the property files in the same directory the user called mantid_setup.m from, not the system's matlab directory - m_strBaseDir = Poco::Path::current(); + m_strBaseDir = Poco::Path::current(); } else { - m_strBaseDir = Mantid::Kernel::getDirectoryOfExecutable(); + m_strBaseDir = Mantid::Kernel::getDirectoryOfExecutable(); } + //attempt to load the default properties file that resides in the directory of the executable - loadConfig( getBaseDir() + m_properties_file_name); - //and then append the user properties - loadConfig( getOutputDir() + m_user_properties_file_name, true); - - //Fill the list of possible relative path keys that may require conversion to absolute paths - m_vConfigPaths.clear(); - m_vConfigPaths.push_back("plugins.directory"); - m_vConfigPaths.push_back("instrumentDefinition.directory"); - m_vConfigPaths.push_back("pythonscripts.directory"); - m_vConfigPaths.push_back("ManagedWorkspace.FilePath"); - - convertRelativePaths(); - - g_log.debug() << "ConfigService created." << std::endl; - g_log.debug() << "Configured base directory of application as " << getBaseDir() << std::endl; - - } - - /// Private copy constructor for singleton class - ConfigServiceImpl::ConfigServiceImpl(const ConfigServiceImpl&) : g_log(Logger::get("ConfigService")) - { - } - - /** Private Destructor - * Prevents client from calling 'delete' on the pointer handed out by Instance - */ - ConfigServiceImpl::~ConfigServiceImpl() - { - Kernel::Logger::shutdown(); - delete m_pSysConfig; - delete m_pConf; // potential double delete??? - } - - /** - * Searches the stored list for keys that have been loaded from the config file and may contain - * relative paths. Any it find are converted to absolute paths and stored separately - */ - void ConfigServiceImpl::convertRelativePaths() - { - if( m_vConfigPaths.empty() ) return; - - std::string execdir(getBaseDir()); - - std::vector<std::string>::const_iterator send = m_vConfigPaths.end(); - for( std::vector<std::string>::const_iterator sitr = m_vConfigPaths.begin(); sitr != send; ++sitr ) - { - if( !m_pConf->hasProperty(*sitr) ) continue; - - std::string value(m_pConf->getString(*sitr)); - try { - if( Poco::Path(value).isRelative() ) - { - m_mAbsolutePaths.insert(std::make_pair(*sitr, Poco::Path(execdir).resolve(value).toString())); - } - } - catch (Poco::PathSyntaxException &ex) - { - g_log.error() << ex.what() << " in .properties file: " << value << std::endl; - } - } - } - - /** - * writes a basic placeholder user.properties file to disk - * any errors are caught and logged, but not propagated - */ - void ConfigServiceImpl::createUserPropertiesFile() const - { - try - { - std::fstream filestr ((getOutputDir() + m_user_properties_file_name).c_str(), std::fstream::out); - - filestr << "# This file can be used to override any properties for this installation." << std::endl; - filestr << "# Any properties found in this file will override any that are found in the Mantid.Properties file" << std::endl; - filestr << "# As this file will not be replaced with futher installations of Mantid it is a safe place to put " << std::endl; - filestr << "# properties that suit your particular installation." << std::endl; - filestr << "" << std::endl; - filestr << "#for example" << std::endl; - filestr << "#uncommenting the line below will set the number of algorithms to retain interim results for to be 90" << std::endl; - filestr << "#overriding any value set in the Mantid.properties file" << std::endl; - filestr << "#algorithms.retained = 90" << std::endl; - - filestr.close(); - } - catch (std::runtime_error ex) - { - g_log.error()<<"Unable to write out user.properties file to " << getOutputDir() << m_user_properties_file_name - << " error: " << ex.what() << std::endl; - } - - } - - /** - * Provides a default Configuration string to use if the config file cannot be loaded. - * @returns The string value of default properties - */ - const std::string ConfigServiceImpl::defaultConfig() const - { - std::string propFile = - "# logging configuration" - "# root level message filter (drop to debug for more messages)" - "logging.loggers.root.level = debug" - "# splitting the messages to many logging channels" - "logging.loggers.root.channel.class = SplitterChannel" - "logging.loggers.root.channel.channel1 = consoleChannel" - "logging.loggers.root.channel.channel2 = fileFilterChannel" - "logging.loggers.root.channel.channel3 = signalChannel" - "# output to the console - primarily for console based apps" - "logging.channels.consoleChannel.class = ConsoleChannel" - "logging.channels.consoleChannel.formatter = f1" - "# specfic filter for the file channel raising the level to warning (drop to debug for debugging)" - "logging.channels.fileFilterChannel.class= FilterChannel" - "logging.channels.fileFilterChannel.channel= fileChannel" - "logging.channels.fileFilterChannel.level= warning" - "# output to a file (For error capturing and debugging)" - "logging.channels.fileChannel.class = debug" - "logging.channels.fileChannel.path = ../logs/mantid.log" - "logging.channels.fileChannel.formatter.class = PatternFormatter" - "logging.channels.fileChannel.formatter.pattern = %Y-%m-%d %H:%M:%S,%i [%I] %p %s - %t" - "logging.formatters.f1.class = PatternFormatter" - "logging.formatters.f1.pattern = %s-[%p] %t" - "logging.formatters.f1.times = UTC;" - "# SignalChannel - Passes messages to the MantidPlot User interface" - "logging.channels.signalChannel.class = SignalChannel"; - return propFile; - } - - - //------------------------------- - // Public member functions - //------------------------------- - - /** Loads the config file provided. - * If the file contains logging setup instructions then these will be used to setup the logging framework. - * - * @param filename The filename and optionally path of the file to load - * @param append If false (default) then any previous configuration is discarded, otherwise the new keys are added, and repeated keys will override existing ones. - */ - void ConfigServiceImpl::loadConfig(const std::string& filename, const bool append) - { - delete m_pConf; - if (!append) - { - //remove the previous property string - m_PropertyString = ""; - } + loadConfig( getBaseDir() + m_properties_file_name); + //and then append the user properties + loadConfig( getOutputDir() + m_user_properties_file_name, true); + + //Fill the list of possible relative path keys that may require conversion to absolute paths + m_vConfigPaths.resize(4, ""); + m_vConfigPaths[0] = "plugins.directory"; + m_vConfigPaths[1] = "instrumentDefinition.directory"; + m_vConfigPaths[2] = "pythonscripts.directory"; + m_vConfigPaths[3] = "ManagedWorkspace.FilePath"; + // Convert the relative paths into absolute ones. Note that the getString call checks whether the requested key is + // one of the above and returns the correct absolute path + convertRelativeToAbsolute(); + + //Configure search paths that have been loaded from the properties file + defineDataSearchPaths(); + + + g_log.debug() << "ConfigService created." << std::endl; + g_log.debug() << "Configured base directory of application as " << getBaseDir() << std::endl; + } + + /// Private copy constructor for singleton class + ConfigServiceImpl::ConfigServiceImpl(const ConfigServiceImpl&) : g_log(Logger::get("ConfigService")) + { + } + + /** Private Destructor + * Prevents client from calling 'delete' on the pointer handed out by Instance + */ + ConfigServiceImpl::~ConfigServiceImpl() + { + Kernel::Logger::shutdown(); + delete m_pSysConfig; + delete m_pConf; // potential double delete??? + } + + /** + * Searches the stored list for keys that have been loaded from the config file and may contain + * relative paths. Any it find are converted to absolute paths and stored separately + */ + void ConfigServiceImpl::convertRelativeToAbsolute() + { + if( m_vConfigPaths.empty() ) return; + + std::string execdir(getBaseDir()); + + std::vector<std::string>::const_iterator send = m_vConfigPaths.end(); + for( std::vector<std::string>::const_iterator sitr = m_vConfigPaths.begin(); sitr != send; ++sitr ) + { + if( !m_pConf->hasProperty(*sitr) ) continue; + + std::string value(m_pConf->getString(*sitr)); + try + { + if( Poco::Path(value).isRelative() ) + { + m_mAbsolutePaths.insert(std::make_pair(*sitr, Poco::Path(execdir).resolve(value).toString())); + } + } + catch (Poco::PathSyntaxException &ex) + { + g_log.error() << ex.what() << " in .properties file: " << value << std::endl; + } + } + } + + /** + * Create the store of data search paths from the 'datasearch.directories' key within the Mantid.properties file. + * The value of the key should be a semi-colon separated list of directories + */ + void ConfigServiceImpl::defineDataSearchPaths() + { + m_vDataSearchDirs.clear(); + std::string paths = getString("datasearch.directories"); + if( paths.empty() ) return; + int options = Poco::StringTokenizer::TOK_TRIM + Poco::StringTokenizer::TOK_IGNORE_EMPTY; + Poco::StringTokenizer tokenizer(paths, ";,", options); + Poco::StringTokenizer::Iterator iend = tokenizer.end(); + m_vDataSearchDirs.reserve(tokenizer.count()); + for( Poco::StringTokenizer::Iterator itr = tokenizer.begin(); itr != iend; ++itr ) + { + Poco::Path token = Poco::Path(*itr).makeDirectory(); + if( Poco::File(token).exists() ) + { + m_vDataSearchDirs.push_back(token.toString()); + } + else + { + g_log.information() << "Ignoring search path \"" << token.toString() << "\", path does not exist.\n"; + } + } + } + + /** + * writes a basic placeholder user.properties file to disk + * any errors are caught and logged, but not propagated + */ + void ConfigServiceImpl::createUserPropertiesFile() const + { + try + { + std::fstream filestr ((getOutputDir() + m_user_properties_file_name).c_str(), std::fstream::out); + + filestr << "# This file can be used to override any properties for this installation." << std::endl; + filestr << "# Any properties found in this file will override any that are found in the Mantid.Properties file" << std::endl; + filestr << "# As this file will not be replaced with futher installations of Mantid it is a safe place to put " << std::endl; + filestr << "# properties that suit your particular installation." << std::endl; + filestr << "" << std::endl; + filestr << "#for example" << std::endl; + filestr << "#uncommenting the line below will set the number of algorithms to retain interim results for to be 90" << std::endl; + filestr << "#overriding any value set in the Mantid.properties file" << std::endl; + filestr << "#algorithms.retained = 90" << std::endl; + + filestr.close(); + } + catch (std::runtime_error ex) + { + g_log.error()<<"Unable to write out user.properties file to " << getOutputDir() << m_user_properties_file_name + << " error: " << ex.what() << std::endl; + } + + } + + /** + * Provides a default Configuration string to use if the config file cannot be loaded. + * @returns The string value of default properties + */ + const std::string ConfigServiceImpl::defaultConfig() const + { + std::string propFile = + "# logging configuration" + "# root level message filter (drop to debug for more messages)" + "logging.loggers.root.level = debug" + "# splitting the messages to many logging channels" + "logging.loggers.root.channel.class = SplitterChannel" + "logging.loggers.root.channel.channel1 = consoleChannel" + "logging.loggers.root.channel.channel2 = fileFilterChannel" + "logging.loggers.root.channel.channel3 = signalChannel" + "# output to the console - primarily for console based apps" + "logging.channels.consoleChannel.class = ConsoleChannel" + "logging.channels.consoleChannel.formatter = f1" + "# specfic filter for the file channel raising the level to warning (drop to debug for debugging)" + "logging.channels.fileFilterChannel.class= FilterChannel" + "logging.channels.fileFilterChannel.channel= fileChannel" + "logging.channels.fileFilterChannel.level= warning" + "# output to a file (For error capturing and debugging)" + "logging.channels.fileChannel.class = debug" + "logging.channels.fileChannel.path = ../logs/mantid.log" + "logging.channels.fileChannel.formatter.class = PatternFormatter" + "logging.channels.fileChannel.formatter.pattern = %Y-%m-%d %H:%M:%S,%i [%I] %p %s - %t" + "logging.formatters.f1.class = PatternFormatter" + "logging.formatters.f1.pattern = %s-[%p] %t" + "logging.formatters.f1.times = UTC;" + "# SignalChannel - Passes messages to the MantidPlot User interface" + "logging.channels.signalChannel.class = SignalChannel"; + return propFile; + } + + + //------------------------------- + // Public member functions + //------------------------------- + + /** Loads the config file provided. + * If the file contains logging setup instructions then these will be used to setup the logging framework. + * + * @param filename The filename and optionally path of the file to load + * @param append If false (default) then any previous configuration is discarded, otherwise the new keys are added, and repeated keys will override existing ones. + */ + void ConfigServiceImpl::loadConfig(const std::string& filename, const bool append) + { + delete m_pConf; + if (!append) + { + //remove the previous property string + m_PropertyString = ""; + } - try - { - std::ifstream propFile(filename.c_str(),std::ios::in); - bool good = propFile.good(); - - //slurp in entire file - extremely unlikely delimter used as an alternate to \n - std::string temp; - getline(propFile,temp,'¬'); - propFile.close(); + try + { + std::ifstream propFile(filename.c_str(),std::ios::in); + bool good = propFile.good(); + + //slurp in entire file - extremely unlikely delimter used as an alternate to \n + std::string temp; + getline(propFile,temp,'¬'); + propFile.close(); - // check if we have failed to open the file - if ((!good) || (temp=="")) - { - if (filename == getOutputDir() + m_user_properties_file_name) - { - //write out a fresh file - createUserPropertiesFile(); - } - else - { - throw Exception::FileError("Cannot open file",filename); - } - } - - //store the property string - if((append) && (m_PropertyString!="")) - { - m_PropertyString = m_PropertyString + "\n" + temp; - } - else - { - m_PropertyString = temp; - } - - } - catch (std::exception& e) - { - //there was a problem loading the file - it probably is not there - std::cerr << "Problem loading the configuration file " << filename << " " << e.what() << std::endl; - - if(!append) - { - // if we have no property values then take the default - m_PropertyString = defaultConfig(); - } - } - - //use the cached property string to initialise the POCO property file - std::istringstream istr(m_PropertyString); - m_pConf = new WrappedObject<Poco::Util::PropertyFileConfiguration>(istr); - - try - { - //Ensure that the logging directory exists - Poco::Path logpath(getString("logging.channels.fileChannel.path")); - if( logpath.toString().empty() || getOutputDir() != getBaseDir() ) - { - std::string logfile = getOutputDir() + "mantid.log"; - logpath.assign(logfile); - m_pConf->setString("logging.channels.fileChannel.path", logfile); - } - //make this path point to the parent directory and create it if it does not exist - logpath.makeParent(); - if( !logpath.toString().empty() ) - { - Poco::File(logpath).createDirectory(); - } - //configure the logging framework - Poco::Util::LoggingConfigurator configurator; - configurator.configure(m_pConf); - } - catch (std::exception& e) - { - std::cerr << "Trouble configuring the logging framework " << e.what()<<std::endl; - } - } - - - /** Searches for a string within the currently loaded configuaration values and - * returns the value as a string. If the key is one of those that was a possible relative path - * then the local store is searched first. - * - * @param keyName The case sensitive name of the property that you need the value of. - * @returns The string value of the property, or an empty string if the key cannot be found - */ - std::string ConfigServiceImpl::getString(const std::string& keyName) - { - std::map<std::string, std::string>::const_iterator mitr = m_mAbsolutePaths.find(keyName); - if( mitr != m_mAbsolutePaths.end() ) - { - return (*mitr).second; - } - std::string retVal; - try - { - retVal = m_pConf->getString(keyName); - } - catch(Poco::NotFoundException&) - { - g_log.debug()<<"Unable to find " << keyName << " in the properties file" << std::endl; - retVal = ""; - } - return retVal; - } - - /** Searches for a string within the currently loaded configuaration values and - * attempts to convert the values to the template type supplied. - * - * @param keyName The case sensitive name of the property that you need the value of. - * @param out The value if found - * @returns A success flag - 0 on failure, 1 on success - */ - template<typename T> - int ConfigServiceImpl::getValue(const std::string& keyName, T& out) - { - std::string strValue = getString(keyName); - int result = StrFunc::convert(strValue,out); - return result; - } - - /** Searches for the string within the environment variables and returns the - * value as a string. - * - * @param keyName The name of the environment variable that you need the value of. - * @returns The string value of the property - */ - std::string ConfigServiceImpl::getEnvironment(const std::string& keyName) - { - return m_pSysConfig->getString("system.env." + keyName); - } - - /** Gets the name of the host operating system - * - * @returns The name pf the OS version - */ - std::string ConfigServiceImpl::getOSName() - { - return m_pSysConfig->getString("system.osName"); - } - - /** Gets the name of the computer running Mantid - * - * @returns The name of the computer - */ - std::string ConfigServiceImpl::getOSArchitecture() - { - return m_pSysConfig->getString("system.osArchitecture"); - } - - /** Gets the name of the operating system Architecture - * - * @returns The operating system architecture - */ - std::string ConfigServiceImpl::getComputerName() - { - return m_pSysConfig->getString("system.nodeName"); - } - - /** Gets the name of the operating system version - * - * @returns The operating system version - */ - std::string ConfigServiceImpl::getOSVersion() - { - return m_pSysConfig->getString("system.osVersion"); - } - - /** Gets the absolute path of the current directory containing the dll - * - * @returns The absolute path of the current directory containing the dll - */ - std::string ConfigServiceImpl::getCurrentDir() - { - return m_pSysConfig->getString("system.currentDir"); - } - - /** Gets the absolute path of the temp directory - * - * @returns The absolute path of the temp directory - */ - std::string ConfigServiceImpl::getTempDir() - { - return m_pSysConfig->getString("system.tempDir"); - } - - /** - * Gets the directory that we consider to be the bse directory. Basically, this is the - * executable directory when running normally or the current directory on startup when - * running through Python on the command line - * @returns The directory to consider as the base directory, including a trailing slash - */ - std::string ConfigServiceImpl::getBaseDir() const - { - return m_strBaseDir; - } + // check if we have failed to open the file + if ((!good) || (temp=="")) + { + if (filename == getOutputDir() + m_user_properties_file_name) + { + //write out a fresh file + createUserPropertiesFile(); + } + else + { + throw Exception::FileError("Cannot open file",filename); + } + } + + //store the property string + if((append) && (m_PropertyString!="")) + { + m_PropertyString = m_PropertyString + "\n" + temp; + } + else + { + m_PropertyString = temp; + } + + } + catch (std::exception& e) + { + //there was a problem loading the file - it probably is not there + std::cerr << "Problem loading the configuration file " << filename << " " << e.what() << std::endl; + + if(!append) + { + // if we have no property values then take the default + m_PropertyString = defaultConfig(); + } + } + + //use the cached property string to initialise the POCO property file + std::istringstream istr(m_PropertyString); + m_pConf = new WrappedObject<Poco::Util::PropertyFileConfiguration>(istr); + + try + { + //Ensure that the logging directory exists + Poco::Path logpath(getString("logging.channels.fileChannel.path")); + if( logpath.toString().empty() || getOutputDir() != getBaseDir() ) + { + std::string logfile = getOutputDir() + "mantid.log"; + logpath.assign(logfile); + m_pConf->setString("logging.channels.fileChannel.path", logfile); + } + //make this path point to the parent directory and create it if it does not exist + logpath.makeParent(); + if( !logpath.toString().empty() ) + { + Poco::File(logpath).createDirectory(); + } + //configure the logging framework + Poco::Util::LoggingConfigurator configurator; + configurator.configure(m_pConf); + } + catch (std::exception& e) + { + std::cerr << "Trouble configuring the logging framework " << e.what()<<std::endl; + } + } + + + /** Searches for a string within the currently loaded configuaration values and + * returns the value as a string. If the key is one of those that was a possible relative path + * then the local store is searched first. + * + * @param keyName The case sensitive name of the property that you need the value of. + * @returns The string value of the property, or an empty string if the key cannot be found + */ + std::string ConfigServiceImpl::getString(const std::string& keyName) + { + std::map<std::string, std::string>::const_iterator mitr = m_mAbsolutePaths.find(keyName); + if( mitr != m_mAbsolutePaths.end() ) + { + return (*mitr).second; + } + std::string retVal; + try + { + retVal = m_pConf->getString(keyName); + } + catch(Poco::NotFoundException&) + { + g_log.debug()<<"Unable to find " << keyName << " in the properties file" << std::endl; + retVal = ""; + } + return retVal; + } + + /** Searches for a string within the currently loaded configuaration values and + * attempts to convert the values to the template type supplied. + * + * @param keyName The case sensitive name of the property that you need the value of. + * @param out The value if found + * @returns A success flag - 0 on failure, 1 on success + */ + template<typename T> + int ConfigServiceImpl::getValue(const std::string& keyName, T& out) + { + std::string strValue = getString(keyName); + int result = StrFunc::convert(strValue,out); + return result; + } + + /** Searches for the string within the environment variables and returns the + * value as a string. + * + * @param keyName The name of the environment variable that you need the value of. + * @returns The string value of the property + */ + std::string ConfigServiceImpl::getEnvironment(const std::string& keyName) + { + return m_pSysConfig->getString("system.env." + keyName); + } + + /** Gets the name of the host operating system + * + * @returns The name pf the OS version + */ + std::string ConfigServiceImpl::getOSName() + { + return m_pSysConfig->getString("system.osName"); + } + + /** Gets the name of the computer running Mantid + * + * @returns The name of the computer + */ + std::string ConfigServiceImpl::getOSArchitecture() + { + return m_pSysConfig->getString("system.osArchitecture"); + } + + /** Gets the name of the operating system Architecture + * + * @returns The operating system architecture + */ + std::string ConfigServiceImpl::getComputerName() + { + return m_pSysConfig->getString("system.nodeName"); + } + + /** Gets the name of the operating system version + * + * @returns The operating system version + */ + std::string ConfigServiceImpl::getOSVersion() + { + return m_pSysConfig->getString("system.osVersion"); + } + + /** Gets the absolute path of the current directory containing the dll + * + * @returns The absolute path of the current directory containing the dll + */ + std::string ConfigServiceImpl::getCurrentDir() + { + return m_pSysConfig->getString("system.currentDir"); + } + + /** Gets the absolute path of the temp directory + * + * @returns The absolute path of the temp directory + */ + std::string ConfigServiceImpl::getTempDir() + { + return m_pSysConfig->getString("system.tempDir"); + } + + /** + * Gets the directory that we consider to be the bse directory. Basically, this is the + * executable directory when running normally or the current directory on startup when + * running through Python on the command line + * @returns The directory to consider as the base directory, including a trailing slash + */ + std::string ConfigServiceImpl::getBaseDir() const + { + return m_strBaseDir; + } - /** - * Return the directory that Mantid should use for writing files. A trailing slash is appended - * so that filenames can more easily be concatenated with this - */ - std::string ConfigServiceImpl::getOutputDir() const - { - #ifdef _WIN32 - return m_strBaseDir; - #else - Poco::Path datadir(m_pSysConfig->getString("system.homeDir")); - datadir.append(".mantid"); - // Create the directory if it doesn't already exist - Poco::File(datadir).createDirectory(); - return datadir.toString() + "/"; - #endif - } - - /// \cond TEMPLATE - - template DLLExport int ConfigServiceImpl::getValue(const std::string&,double&); - template DLLExport int ConfigServiceImpl::getValue(const std::string&,std::string&); - template DLLExport int ConfigServiceImpl::getValue(const std::string&,int&); - - /// \endcond TEMPLATE - - } // namespace Kernel + /** + * Return the directory that Mantid should use for writing files. A trailing slash is appended + * so that filenames can more easily be concatenated with this + */ + std::string ConfigServiceImpl::getOutputDir() const + { +#ifdef _WIN32 + return m_strBaseDir; +#else + Poco::Path datadir(m_pSysConfig->getString("system.homeDir")); + datadir.append(".mantid"); + // Create the directory if it doesn't already exist + Poco::File(datadir).createDirectory(); + return datadir.toString() + "/"; +#endif + } + + const std::vector<std::string>& ConfigServiceImpl::getDataSearchDirs() const + { + return m_vDataSearchDirs; + } + + /// \cond TEMPLATE + + template DLLExport int ConfigServiceImpl::getValue(const std::string&,double&); + template DLLExport int ConfigServiceImpl::getValue(const std::string&,std::string&); + template DLLExport int ConfigServiceImpl::getValue(const std::string&,int&); + + /// \endcond TEMPLATE + + } // namespace Kernel } // namespace Mantid diff --git a/Code/Mantid/Kernel/src/FileProperty.cpp b/Code/Mantid/Kernel/src/FileProperty.cpp new file mode 100644 index 0000000000000000000000000000000000000000..527c3691846feb407764227a52311106d3d47877 --- /dev/null +++ b/Code/Mantid/Kernel/src/FileProperty.cpp @@ -0,0 +1,90 @@ +//----------------------------------------------------------------- +// Includes +//----------------------------------------------------------------- +#include "MantidKernel/FileProperty.h" +#include "MantidKernel/ConfigService.h" +#include "Poco/Path.h" + +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 A vector containing the 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, + const std::vector<std::string> & exts, unsigned int direction) + : PropertyWithValue<std::string>(name, default_value, new FileValidator(exts, action), direction), + m_action(action) +{ +} + +/** + * Set the filename + * @param filename The value here is treated as a filename. + * @returns A string indicating the outcome of the attempt to set the property. An empty string indicates success. + */ +std::string FileProperty::setValue(const std::string & filename) +{ + // If the path is absolute then don't do any searching + if( Poco::Path(filename).isAbsolute() ) + { + return PropertyWithValue<std::string>::setValue(filename); + } + + std::string valid_string(""); + // For relative paths, differentiate between load and save types + if( m_action == FileProperty::Load ) + { + Poco::File relative(filename); + Poco::File check_file(Poco::Path(Poco::Path::current()).resolve(relative.path())); + //Do a quick check relative to the current directory first + if( check_file.exists() ) + { + valid_string = PropertyWithValue<std::string>::setValue(check_file.path()); + } + else + { + const std::vector<std::string>& search_dirs = ConfigService::Instance().getDataSearchDirs(); + std::vector<std::string>::const_iterator iend = search_dirs.end(); + for( std::vector<std::string>::const_iterator it = search_dirs.begin(); it != iend; ++it ) + { + check_file = Poco::File(Poco::Path(*it).resolve(relative.path())); + if( check_file.exists() ) + { + valid_string = PropertyWithValue<std::string>::setValue(check_file.path()); + break; + } + } + } + } + else + { + // We have a relative save path so just prepend the path that is in the 'defaultsave.directory' + std::string save_path = ConfigService::Instance().getString("defaultsave.directory"); + Poco::Path save_dir; + if( save_path.empty() ) + { + save_dir = Poco::Path(filename).parent(); + } + else + { + save_dir = Poco::Path(save_path).makeDirectory(); + } + if( Poco::File(save_dir).canWrite() ) + { + valid_string = PropertyWithValue<std::string>::setValue(save_dir.resolve(filename).toString()); + } + else + { + valid_string = "Cannot write file to path \"" + save_dir.toString() + "\". Location is not writable."; + } + } + return valid_string; +} diff --git a/Code/Mantid/Properties/Mantid.properties b/Code/Mantid/Properties/Mantid.properties index fac68d0d79e101bcd3fbc08dda465ffa6f08168c..6e49c075a051ddf3791a348ed71931e310119c3e 100644 --- a/Code/Mantid/Properties/Mantid.properties +++ b/Code/Mantid/Properties/Mantid.properties @@ -8,6 +8,12 @@ instrumentDefinition.directory = ../../../../Test/Instrument # Python API so that Mantid scripts can be imported more easily pythonscripts.directory = ../../PythonAPI/scripts +# A semi-colon(;) separated list of directories to use to search for data +datasearch.directories = + +# A default directory to use for saving files +defaultsave.directory = + # # ManagedWorkspace.LowerMemoryLimit sets the memory limit to trigger the use of # a ManagedWorkspace. A ManagedWorkspace will be used for a workspace requiring greater amount of memory @@ -53,4 +59,4 @@ logging.formatters.f1.class = PatternFormatter logging.formatters.f1.pattern = %s-[%p] %t logging.formatters.f1.times = UTC; # SignalChannel - Passes messages to the MantidPlot User interface -logging.channels.signalChannel.class = SignalChannel \ No newline at end of file +logging.channels.signalChannel.class = SignalChannel